grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

grub-emu : Dynamic loading of modules & GDB interface


From: Eric Salomé
Subject: grub-emu : Dynamic loading of modules & GDB interface
Date: Fri, 26 May 2006 03:24:53 +0200

Hi Lubomir, Marco, Hi Folks,

I've come to a "steady" point on my work about grub-emu dynamic loading
of modules. I am open to new ideas as they come.
Your comments are welcomed.

Here are the diff from grub 1.93 relating all the changes I have made so
far.
Some may be useful to others. Fell free to add them to grub if needed.

        (1) - Enable dynamically loading of modules in grub-emu
              for the i386-pc.
        (2) - Provide changes for GDB to operate properly when loading
modules
              in grub-emu. GRUB/GDB interface for dynamic loading of
modules.
         (I believe that there is a few line changes
          that would be helpful for Lubomir Kundrak scripts.
          At least those few in dl.c that store the name of each ELF
section
          in mod->seg->name when loading modules). See also Nota Bene.
        (3) - Add two or three functions I needed for other modules.
              You might not need those changes.
        (4) - Minor/Other changes

You can browse the changes looking at "FILE" at start of line.
or searching for (1), (2), (3), (4) tokens or look for "Nota Bene".

There is one file outside Grub distribution (see FILE
gdb-6.1/gdb/solib-svr4.c)

Have a nice day.
Best Regards,

Eric Salomé - Paris, France

PS: Is there anyone working on bringing a VBE interface to grub-emu ?




FILE conf/i386-pc.rmk

        (1) grub-emu includes kern/i386/dl.c and symlist2.c
            to be able to properly load modules.

        (1) I don't think it possible to dyn load modules in grub-emu
with
            functions passing parameters in registers. So I compile
without
            related options and I have adapted ASM files (those I know
of)
            for both cases.
            Note that it still is possible later to do a final build run
            before/after distribution with the -mrtd -mregparm=3
options.
            So that everybody is happy (I hope).

        #define REGPARM is used in ASM files to let them know they are
        been compiled with or without passing parameters in registers.
        (see setjmp.S for an example).

        (1),(2) Add compile options for different cases

        (2) file kern/dl-gdb.c (NEW) is an example of grub_gdb...()
functions
            (GRUB/GDB interface for dynamically loading of modules)
            to build  "module.elf 0x321213 -s .data 0x321435 ..." string
in
            mod->elf for example, or to send a "signal" to gdb to
advertize
            for loads /unloads of modules.


diff -EbwBruN ./conf/i386-pc.rmk ../../src/grub-1.93/conf/i386-pc.rmk
--- ./conf/i386-pc.rmk  2005-12-25 16:59:50.000000000 +0100
+++ ../../src/grub-1.93/conf/i386-pc.rmk        2006-05-25
17:20:24.000000000 +0200
@@ -1,7 +1,25 @@
 # -*- makefile -*-
 
+# For final built, passing parameters in registers
+# That's about 2 % shorter code, and may be 10 % faster.
+
+#COMMON_ASFLAGS = -nostdinc -fno-builtin -DREGPARM
+#COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
+
+# For building grub-emu to be run with a modified GDB (6.1) that allows
+# ELF sections to be loaded at non-contigus memory locations
+
 COMMON_ASFLAGS = -nostdinc -fno-builtin
-COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
+COMMON_CFLAGS = -fno-builtin -m32 -DGRUB_COMPAT_GDB
+
+# For standard building of grub-emu when no modified GDB is available
+# See following links for help in debugging with GRUB / GDB
+# http://NetBSD.sk/~lkundrak/misc/grub2-gdb/howto.html
+
+#COMMON_ASFLAGS = -nostdinc -fno-builtin
+#COMMON_CFLAGS = -fno-builtin -m32 
+
+
 COMMON_LDFLAGS = -melf_i386
 
 # Images.
@@ -89,6 +107,7 @@
        fs/jfs.c fs/minix.c fs/sfs.c fs/ufs.c fs/xfs.c fs/hfsplus.c
\
        io/gzio.c
\
        kern/device.c kern/disk.c kern/dl.c kern/env.c kern/err.c
\
+       kern/dl-gdb.c
\
        normal/execute.c kern/file.c kern/fs.c normal/lexer.c
\
        kern/loader.c kern/main.c kern/misc.c kern/parser.c
\
        grub_script.tab.c kern/partition.c kern/rescue.c kern/term.c
\
@@ -99,8 +118,9 @@
        partmap/acorn.c partmap/gpt.c
\
        util/console.c util/grub-emu.c util/misc.c
\
        util/i386/pc/biosdisk.c util/i386/pc/getroot.c
\
-       util/i386/pc/misc.c grub_emu_init.c
+       util/i386/pc/misc.c grub_emu_init.c kern/i386/dl.c symlist2.c
 
+grub_emu_CFLAGS = $(COMMON_CFLAGS)
 grub_emu_LDFLAGS = $(LIBCURSES)
 

FILE normal/i386/setjmp.S

        (1) Now use the #define REGPARM to tell if parameters are
expected
            in registers, or in the stack.

diff -EbwBruN ./normal/i386/setjmp.S
../../src/grub-1.93/normal/i386/setjmp.S
--- ./normal/i386/setjmp.S      2004-04-04 15:46:03.000000000 +0200
+++ ../../src/grub-1.93/normal/i386/setjmp.S    2006-05-08
23:14:41.000000000 +0200
@@ -27,6 +27,9 @@
  * int grub_setjmp (grub_jmp_buf env)
  */
 FUNCTION(grub_setjmp)
+#ifndef REGPARM
+       movl    4(%esp), %eax   /* Get Param */
+#endif
        movl    %ebx, 0(%eax)   /* EBX */
        movl    %esi, 4(%eax)   /* ESI */
        movl    %edi, 8(%eax)   /* EDI */
@@ -42,6 +45,10 @@
  * int grub_longjmp (grub_jmp_buf env, int val)
  */
 FUNCTION(grub_longjmp)
+#ifndef REGPARM
+       movl    4(%esp), %eax   /* Get Param */
+       movl    8(%esp), %edx   /* Get Param */
+#endif
        movl    0(%eax), %ebx
        movl    4(%eax), %esi
        movl    8(%eax), %edi

FILE kern/i386/pc/startup.S

        (1) ASM file moved to startup.S.regparm
            A new ASM file is also provided startup.S.memparm to be used
            when you compile without -mrtd -mregparm=3 options.
            
diff -EbwBruN ./kern/i386/pc/startup.S
../../src/grub-1.93/kern/i386/pc/startup.S
--- ./kern/i386/pc/startup.S    2005-10-15 19:28:36.000000000 +0200
+++ ../../src/grub-1.93/kern/i386/pc/startup.S  2006-05-08
23:11:49.000000000 +0200
@@ -17,2154 +17,9 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#ifdef REGPARM
+#include "startup.S.regparm"
+#else
+#include "startup.S.memparm"
+#endif
 
-/*
- * Note: These functions defined in this file may be called from C.
- *       Be careful of that you must not modify some registers. Quote
- *       from gcc-2.95.2/gcc/config/i386/i386.h:
-       
-   1 for registers not available across function calls.
-   These must include the FIXED_REGISTERS and also any
-   registers that can be used without being saved.
-   The latter must include the registers where values are returned
-   and the register where structure-value addresses are passed.
-   Aside from that, you can include as many other registers as you
like.
-
-  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
-{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
- */
-
-/*
- * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
- *       So the first three arguments are passed in %eax, %edx, and
%ecx,
- *       respectively, and if a function has a fixed number of
arguments
- *       and the number if greater than three, the function must return
- *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
- */
-
-#include <config.h>
-#include <grub/symbol.h>
-#include <grub/boot.h>
-#include <grub/machine/boot.h>
-#include <grub/machine/memory.h>
-#include <grub/machine/console.h>
-#include <grub/machine/linux.h>
-#include <grub/machine/kernel.h>
-#include <grub/machine/multiboot.h>
-               
-#define ABS(x) ((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR +
0x200)
-       
-       .file   "startup.S"
-
-       .text
-
-       /* Tell GAS to generate 16-bit instructions so that this code
works
-          in real mode. */
-       .code16
-
-       .globl  start, _start
-start:
-_start:
-       /*
-        *  Guarantee that "main" is loaded at 0x0:0x8200.
-        */
-       ljmp $0, $ABS(codestart)
-
-       /*
-        *  Compatibility version number
-        *
-        *  These MUST be at byte offset 6 and 7 of the executable
-        *  DO NOT MOVE !!!
-        */
-       . = EXT_C(start) + 0x6
-       .byte   GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
-
-       /*
-        *  This is a special data area 8 bytes from the beginning.
-        */
-
-       . = EXT_C(start) + 0x8
-
-VARIABLE(grub_total_module_size)
-       .long   0
-VARIABLE(grub_kernel_image_size)
-       .long   0
-VARIABLE(grub_compressed_size)
-       .long   0
-VARIABLE(grub_install_dos_part)
-       .long   0xFFFFFFFF
-VARIABLE(grub_install_bsd_part)
-       .long   0xFFFFFFFF
-VARIABLE(grub_prefix)
-       .string "/boot/grub"
-
-       /*
-        *  Leave some breathing room for the prefix.
-        */
-
-       . = EXT_C(start) + 0x50
-
-/*
- * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB
itself).
- * This uses the a.out kludge to load raw binary to the area starting
at 1MB,
- * and relocates itself after loaded.
- */
-multiboot_header:
-       /* magic */
-       .long   0x1BADB002
-       /* flags */
-       .long   (1 << 16)
-       /* checksum */
-       .long   -0x1BADB002 - (1 << 16)
-       /* header addr */
-       .long   multiboot_header - _start + 0x100000 + 0x200
-       /* load addr */
-       .long   0x100000
-       /* load end addr */
-       .long   0
-       /* bss end addr */
-       .long   0
-       /* entry addr */
-       .long   multiboot_entry - _start + 0x100000 + 0x200
-       
-multiboot_entry:
-       .code32
-       /* obtain the boot device */
-       movl    12(%ebx), %edx
-
-       /* relocate the code */
-       movl    $(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx
-       addl    EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200,
%ecx
-       movl    $0x100000, %esi
-       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi
-       cld
-       rep
-       movsb
-       /* jump to the real address */
-       movl    $multiboot_trampoline, %eax
-       jmp     *%eax
-       
-multiboot_trampoline:
-       /* fill the boot information */
-       movl    %edx, %eax
-       shrl    $8, %eax
-       xorl    %ebx, %ebx
-       cmpb    $0xFF, %ah
-       je      1f
-       movb    %ah, %bl
-       movl    %ebx, EXT_C(grub_install_dos_part)
-1:
-       cmpb    $0xFF, %al
-       je      2f
-       movb    %al, %bl
-       movl    %ebx, EXT_C(grub_install_bsd_part)
-2:
-       shrl    $24, %edx
-       /* enter the usual booting */
-       call    prot_to_real
-       .code16
-       
-/* the real mode code continues... */
-codestart:
-       cli             /* we're not safe here! */
-
-       /* set up %ds, %ss, and %es */
-       xorw    %ax, %ax
-       movw    %ax, %ds
-       movw    %ax, %ss
-       movw    %ax, %es
-
-       /* set up the real mode/BIOS stack */
-       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
-       movl    %ebp, %esp
-
-       sti             /* we're safe again */
-
-       /* save boot drive reference */
-       ADDR32  movb    %dl, EXT_C(grub_boot_drive)
-
-       /* reset disk system (%ah = 0) */
-       int     $0x13
-       
-       /* transition to protected mode */
-       DATA32  call real_to_prot
-
-       /* The ".code32" directive takes GAS out of 16-bit mode. */
-       .code32
-
-       incl    %eax
-       call    EXT_C(grub_gate_a20)
-       
-       /* decompress the compressed part and put the result at 1MB */
-       movl    $0x100000, %esi
-       movl    $(START_SYMBOL + GRUB_KERNEL_MACHINE_RAW_SIZE), %edi
-
-       pushl   %esi
-       pushl   EXT_C(grub_compressed_size)
-       pushl   %edi
-       call    lzo1x_decompress
-       addl    $12, %esp
-
-       /* copy back the decompressed part */
-       movl    %eax, %ecx
-       cld
-       rep
-       movsb
-
-       /* copy modules before cleaning out the bss */
-       movl    EXT_C(grub_total_module_size), %ecx
-       movl    EXT_C(grub_kernel_image_size), %esi
-       addl    %ecx, %esi
-       addl    $START_SYMBOL, %esi
-       decl    %esi
-       movl    $END_SYMBOL, %edi
-       addl    %ecx, %edi
-       decl    %edi
-       std
-       rep
-       movsb
-       
-       /* clean out the bss */
-       movl    $BSS_START_SYMBOL, %edi
-
-       /* compute the bss length */
-       movl    $END_SYMBOL, %ecx
-       subl    %edi, %ecx
-                       
-       /* clean out */
-       xorl    %eax, %eax
-       cld
-       rep
-       stosb
-       
-       /*
-        *  Call the start of main body of C code.
-        */
-       call EXT_C(grub_main)
-
-
-/*
- *  This is the area for all of the special variables.
- */
-
-       .p2align        2       /* force 4-byte alignment */
-
-protstack:
-       .long   GRUB_MEMORY_MACHINE_PROT_STACK
-
-VARIABLE(grub_boot_drive)
-       .long   0
-
-VARIABLE(grub_start_addr)
-       .long   START_SYMBOL
-
-VARIABLE(grub_end_addr)
-       .long   END_SYMBOL
-       
-VARIABLE(grub_apm_bios_info)
-       .word   0       /* version */
-       .word   0       /* cseg */
-       .long   0       /* offset */
-       .word   0       /* cseg_16 */
-       .word   0       /* dseg_16 */
-       .word   0       /* cseg_len */
-       .word   0       /* cseg_16_len */
-       .word   0       /* dseg_16_len */
-       
-/*
- * This is the Global Descriptor Table
- *
- *  An entry, a "Segment Descriptor", looks like this:
- *
- * 31          24         19   16                 7           0
- * ------------------------------------------------------------
- * |             | |B| |A|       | |   |1|0|E|W|A|            |
- * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |  4
- * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
- * ------------------------------------------------------------
- * |                             |                            |
- * |        BASE 15..0           |       LIMIT 15..0          |  0
- * |                             |                            |
- * ------------------------------------------------------------
- *
- *  Note the ordering of the data items is reversed from the above
- *  description.
- */
-
-       .p2align        2       /* force 4-byte alignment */
-gdt:
-       .word   0, 0
-       .byte   0, 0, 0, 0
-
-       /* -- code segment --
-        * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity),
present
-        * type = 32bit code execute/read, DPL = 0
-        */
-       .word   0xFFFF, 0
-       .byte   0, 0x9A, 0xCF, 0
-
-       /* -- data segment --
-        * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
-        * type = 32 bit data read/write, DPL = 0 
-        */
-       .word   0xFFFF, 0
-       .byte   0, 0x92, 0xCF, 0
-
-       /* -- 16 bit real mode CS --
-        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
-        * type = 16 bit code execute/read only/conforming, DPL = 0
-        */
-       .word   0xFFFF, 0
-       .byte   0, 0x9E, 0, 0
-
-       /* -- 16 bit real mode DS --
-        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
-        * type = 16 bit data read/write, DPL = 0
-        */
-       .word   0xFFFF, 0
-       .byte   0, 0x92, 0, 0
-
-
-/* this is the GDT descriptor */
-gdtdesc:
-       .word   0x27                    /* limit */
-       .long   gdt                     /* addr */
-
-       
-/*
- *  These next two routines, "real_to_prot" and "prot_to_real" are
structured
- *  in a very specific way.  Be very careful when changing them.
- *
- *  NOTE:  Use of either one messes up %eax and %ebp.
- */
-
-real_to_prot:
-       .code16
-       cli
-
-       /* load the GDT register */
-       DATA32  ADDR32  lgdt    gdtdesc
-
-       /* turn on protected mode */
-       movl    %cr0, %eax
-       orl     $GRUB_MEMORY_MACHINE_CR0_PE_ON, %eax
-       movl    %eax, %cr0
-
-       /* jump to relocation, flush prefetch queue, and reload %cs */
-       DATA32  ljmp    $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
-
-       .code32
-protcseg:
-       /* reload other segment registers */
-       movw    $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
-       movw    %ax, %ds
-       movw    %ax, %es
-       movw    %ax, %fs
-       movw    %ax, %gs
-       movw    %ax, %ss
-
-       /* put the return address in a known safe location */
-       movl    (%esp), %eax
-       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
-
-       /* get protected mode stack */
-       movl    protstack, %eax
-       movl    %eax, %esp
-       movl    %eax, %ebp
-
-       /* get return address onto the right stack */
-       movl    GRUB_MEMORY_MACHINE_REAL_STACK, %eax
-       movl    %eax, (%esp)
-
-       /* zero %eax */
-       xorl    %eax, %eax
-
-       /* return on the old (or initialized) stack! */
-       ret
-
-
-prot_to_real:
-       /* just in case, set GDT */
-       lgdt    gdtdesc
-
-       /* save the protected mode stack */
-       movl    %esp, %eax
-       movl    %eax, protstack
-
-       /* get the return address */
-       movl    (%esp), %eax
-       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
-
-       /* set up new stack */
-       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
-       movl    %eax, %esp
-       movl    %eax, %ebp
-
-       /* set up segment limits */
-       movw    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
-       movw    %ax, %ds
-       movw    %ax, %es
-       movw    %ax, %fs
-       movw    %ax, %gs
-       movw    %ax, %ss
-
-       /* this might be an extra step */
-       /* jump to a 16 bit segment */
-       ljmp    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
-
-tmpcseg:
-       .code16
-
-       /* clear the PE bit of CR0 */
-       movl    %cr0, %eax
-       andl    $(~GRUB_MEMORY_MACHINE_CR0_PE_ON), %eax
-       movl    %eax, %cr0
-
-       /* flush prefetch queue, reload %cs */
-       DATA32  ljmp    $0, $realcseg
-
-realcseg:
-       /* we are in real mode now
-        * set up the real mode segment registers : DS, SS, ES
-        */
-       /* zero %eax */
-       xorl    %eax, %eax
-
-       movw    %ax, %ds
-       movw    %ax, %es
-       movw    %ax, %fs
-       movw    %ax, %gs
-       movw    %ax, %ss
-
-       /* restore interrupts */
-       sti
-
-       /* return on new stack! */
-       DATA32  ret
-
-       .code32
-
-
-/*
- * grub_gate_a20(int on)
- *
- * Gate address-line 20 for high memory.
- *
- * This routine is probably overconservative in what it does, but so
what?
- *
- * It also eats any keystrokes in the keyboard buffer.  :-(
- */
-
-FUNCTION(grub_gate_a20)
-       movl    %eax, %edx
-
-gate_a20_test_current_state:   
-       /* first of all, test if already in a good state */
-       call    gate_a20_check_state
-       cmpb    %al, %dl
-       jnz     gate_a20_try_bios
-       ret
-
-gate_a20_try_bios:
-       /* second, try a BIOS call */
-       pushl   %ebp
-       call    prot_to_real
-
-       .code16
-       movw    $0x2400, %ax
-       testb   %dl, %dl
-       jz      1f
-       incw    %ax
-1:     int     $0x15
-
-       DATA32  call    real_to_prot
-       .code32
-
-       popl    %ebp
-       call    gate_a20_check_state
-       cmpb    %al, %dl
-       jnz     gate_a20_try_keyboard_controller
-       ret
-       
-gate_a20_flush_keyboard_buffer:
-       inb     $0x64
-       andb    $0x02, %al
-       jnz     gate_a20_flush_keyboard_buffer
-2:     
-       inb     $0x64
-       andb    $0x01, %al
-       jz      3f
-       inb     $0x60
-       jmp     2b
-3:
-       ret
-       
-gate_a20_try_keyboard_controller:      
-       /* third, try the keyboard controller */
-       call    gate_a20_flush_keyboard_buffer
-
-       movb    $0xd1, %al
-       outb    $0x64
-4:     
-       inb     $0x64
-       andb    $0x02, %al
-       jnz     4b
-
-       movb    $0xdd, %al
-       testb   %dl, %dl
-       jz      5f
-       orb     $0x02, %al
-5:     outb    $0x60
-       call    gate_a20_flush_keyboard_buffer
-
-       /* output a dummy command (USB keyboard hack) */
-       movb    $0xff, %al
-       outb    $0x64
-       call    gate_a20_flush_keyboard_buffer
-
-       call    gate_a20_check_state
-       cmpb    %al, %dl
-       jnz     gate_a20_try_system_control_port_a
-       ret
-
-gate_a20_try_system_control_port_a:
-       /* fourth, try the system control port A */
-       inb     $0x92
-       andb    $(~0x03), %al
-       testb   %dl, %dl
-       jz      6f
-       orb     $0x02, %al
-6:     outb    $0x92
-
-       /* When turning off Gate A20, do not check the state strictly,
-          because a failure is not fatal usually, and Gate A20 is
always
-          on some modern machines.  */
-       testb   %dl, %dl
-       jz      7f      
-       call    gate_a20_check_state
-       cmpb    %al, %dl
-       /* everything failed, so restart from the beginning */
-       jnz     gate_a20_try_bios
-7:     ret
-       
-gate_a20_check_state:
-       /* iterate the checking for a while */
-       movl    $100, %ecx
-1:     
-       call    3f
-       cmpb    %al, %dl
-       jz      2f
-       loop    1b
-2:
-       ret
-3:     
-       pushl   %ebx
-       pushl   %ecx
-       xorl    %eax, %eax
-       /* compare the byte at 0x8000 with that at 0x108000 */
-       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx
-       pushl   %ebx
-       /* save the original byte in CL */
-       movb    (%ebx), %cl
-       /* store the value at 0x108000 in AL */
-       addl    $0x100000, %ebx
-       movb    (%ebx), %al
-       /* try to set one less value at 0x8000 */
-       popl    %ebx
-       movb    %al, %ch
-       decb    %ch
-       movb    %ch, (%ebx)
-       /* serialize */
-       outb    %al, $0x80
-       outb    %al, $0x80
-       /* obtain the value at 0x108000 in CH */
-       pushl   %ebx
-       addl    $0x100000, %ebx
-       movb    (%ebx), %ch
-       /* this result is 1 if A20 is on or 0 if it is off */
-       subb    %ch, %al
-       xorb    $1, %al
-       /* restore the original */
-       popl    %ebx
-       movb    %cl, (%ebx)
-       popl    %ecx
-       popl    %ebx
-       ret
-
-#include "lzo1x.S"
-
-
-/*
- *  This call is special...  it never returns...  in fact it should
simply
- *  hang at this point!
- */
-
-FUNCTION(grub_stop)
-       call    prot_to_real
-
-       /*
-        * This next part is sort of evil.  It takes advantage of the
-        * byte ordering on the x86 to work in either 16-bit or 32-bit
-        * mode, so think about it before changing it.
-        */
-
-FUNCTION(grub_hard_stop)
-       hlt
-       jmp EXT_C(grub_hard_stop)
-
-
-/*
- * grub_stop_floppy()
- *
- * Stop the floppy drive from spinning, so that other software is
- * jumped to with a known state.
- */
-FUNCTION(grub_stop_floppy)
-       movw    $0x3F2, %dx
-       xorb    %al, %al
-       outb    %al, %dx
-       ret
-
-/*
- * grub_reboot()
- *
- * Reboot the system. At the moment, rely on BIOS.
- */
-FUNCTION(grub_reboot)
-       call    prot_to_real
-       .code16
-       /* cold boot */
-       movw    $0x0472, %di
-       movw    %ax, (%di)
-       ljmp    $0xFFFF, $0x0000
-       .code32
-       
-/*
- * grub_halt(int no_apm)
- *
- * Halt the system, using APM if possible. If NO_APM is true, don't use
- * APM even if it is available.
- */
-FUNCTION(grub_halt)
-       /* see if zero */
-       testl   %eax, %eax
-       jnz     EXT_C(grub_stop)
-
-       call    prot_to_real
-       .code16
-       
-       /* detect APM */
-       movw    $0x5300, %ax
-       xorw    %bx, %bx
-       int     $0x15
-       jc      EXT_C(grub_hard_stop)
-       /* don't check %bx for buggy BIOSes... */
-
-       /* disconnect APM first */
-       movw    $0x5304, %ax
-       xorw    %bx, %bx
-       int     $0x15
-
-       /* connect APM */
-       movw    $0x5301, %ax
-       xorw    %bx, %bx
-       int     $0x15
-       jc      EXT_C(grub_hard_stop)
-
-       /* set APM protocol level - 1.1 or bust. (this covers APM 1.2
also) */
-       movw    $0x530E, %ax
-       xorw    %bx, %bx
-       movw    $0x0101, %cx
-       int     $0x15
-       jc      EXT_C(grub_hard_stop)
-       
-       /* set the power state to off */
-       movw    $0x5307, %ax
-       movw    $1, %bx
-       movw    $3, %cx
-       int     $0x15
-
-       /* shouldn't reach here */
-       jmp     EXT_C(grub_hard_stop)
-       .code32
-       
-       
-/*
- *  void grub_chainloader_real_boot (int drive, void *part_addr)
- *
- *  This starts another boot loader.
- */
-
-FUNCTION(grub_chainloader_real_boot)
-       pushl   %edx
-       pushl   %eax
-
-       call    EXT_C(grub_dl_unload_all)
-
-       /* set up to pass boot drive */
-       popl    %edx
-
-       /* ESI must point to a partition table entry */
-       popl    %esi
-
-       /* Turn off Gate A20 */
-       xorl    %eax, %eax
-       call    EXT_C(grub_gate_a20)
-       
-       call    prot_to_real
-       .code16
-       ljmp    $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
-       .code32
-
-
-/*
- * void grub_linux_boot_zimage (void)
- */
-VARIABLE(grub_linux_prot_size)
-       .long   0
-VARIABLE(grub_linux_tmp_addr)
-       .long   0
-VARIABLE(grub_linux_real_addr)
-       .long   0
-       
-FUNCTION(grub_linux_boot_zimage)
-       /* copy the kernel */
-       movl    EXT_C(grub_linux_prot_size), %ecx
-       addl    $3, %ecx
-       shrl    $2, %ecx
-       movl    $GRUB_LINUX_BZIMAGE_ADDR, %esi
-       movl    $GRUB_LINUX_ZIMAGE_ADDR, %edi
-       cld
-       rep
-       movsl
-
-FUNCTION(grub_linux_boot_bzimage)
-       call    EXT_C(grub_dl_unload_all)
-       
-       movl    EXT_C(grub_linux_real_addr), %ebx
-
-       /* copy the real mode code */
-       movl    EXT_C(grub_linux_tmp_addr), %esi
-       movl    %ebx, %edi
-       movl    $GRUB_LINUX_SETUP_MOVE_SIZE, %ecx
-       cld
-       rep
-       movsb
-
-       /* change %ebx to the segment address */
-       shrl    $4, %ebx
-       movl    %ebx, %eax
-       addl    $0x20, %eax
-       movw    %ax, linux_setup_seg
-
-       /* XXX new stack pointer in safe area for calling functions */
-       movl    $0x4000, %esp
-       call    EXT_C(grub_stop_floppy)
-
-       /* final setup for linux boot */
-       call    prot_to_real
-       .code16
-
-       cli
-       movw    %bx, %ss
-       movw    $GRUB_LINUX_SETUP_STACK, %sp
-
-       movw    %bx, %ds
-       movw    %bx, %es
-       movw    %bx, %fs
-       movw    %bx, %gs
-
-       /* ljmp */
-       .byte   0xea
-       .word   0
-linux_setup_seg:
-       .word   0
-       .code32
-
-               
-/*
- * This starts the multiboot kernel.
- */
-
-FUNCTION(grub_multiboot_real_boot)
-       /* Push the entry address on the stack.  */
-       pushl   %eax
-       /* Move the address of the multiboot information structure to
ebx.  */
-       movl    %edx,%ebx
-       
-       /* Unload all modules and stop the floppy driver.  */
-       call    EXT_C(grub_dl_unload_all)
-       call    EXT_C(grub_stop_floppy)
-
-       /* Interrupts should be disabled.  */
-       cli
-       
-       /* Move the magic value into eax and jump to the kernel.  */
-       movl    $GRUB_MB_MAGIC2,%eax
-       popl    %ecx
-       jmp     *%ecx
-       
-       
-/*
- *   int grub_biosdisk_rw_int13_extensions (int ah, int drive, void
*dap)
- *
- *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
- *   is passed for disk address packet. If an error occurs, return
- *   non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_rw_int13_extensions)
-       pushl   %ebp
-       pushl   %esi
-
-       /* compute the address of disk_address_packet */
-       movw    %cx, %si
-       xorw    %cx, %cx
-       shrl    $4, %ecx        /* save the segment to cx */
-
-       /* ah */
-       movb    %al, %dh
-       /* enter real mode */
-       call    prot_to_real
-       
-       .code16
-       movb    %dh, %ah
-       movw    %cx, %ds
-       int     $0x13           /* do the operation */
-       movb    %ah, %dl        /* save return value */
-       /* clear the data segment */
-       xorw    %ax, %ax
-       movw    %ax, %ds
-       /* back to protected mode */
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %dl, %al        /* return value in %eax */
-
-       popl    %esi
-       popl    %ebp
-
-       ret
-       
-/*
- *   int grub_biosdisk_rw_standard (int ah, int drive, int coff, int
hoff,
- *                                  int soff, int nsec, int segment)
- *
- *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
- *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
- *   return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_rw_standard)
-       pushl   %ebp
-       movl    %esp, %ebp
-
-       pushl   %ebx
-       pushl   %edi
-       pushl   %esi
-
-       /* set up CHS information */
-
-       /* set %ch to low eight bits of cylinder */
-       xchgb   %cl, %ch
-       /* set bits 6-7 of %cl to high two bits of cylinder */
-       shlb    $6, %cl
-       /* set bits 0-5 of %cl to sector */
-       addb    0xc(%ebp), %cl
-       /* set %dh to head */
-       movb    0x8(%ebp), %dh
-       /* set %ah to AH */
-       movb    %al, %ah
-       /* set %al to NSEC */
-       movb    0x10(%ebp), %al
-       /* save %ax in %di */
-       movw    %ax, %di
-       /* save SEGMENT in %bx */
-       movw    0x14(%ebp), %bx
-               
-       /* enter real mode */
-       call    prot_to_real
-
-       .code16
-       movw    %bx, %es
-       xorw    %bx, %bx
-       movw    $3, %si         /* attempt at least three times */
-
-1:     
-       movw    %di, %ax
-       int     $0x13           /* do the operation */
-       jnc     2f              /* check if successful */
-
-       movb    %ah, %bl        /* save return value */
-       /* if fail, reset the disk system */
-       xorw    %ax, %ax
-       int     $0x13
-       
-       decw    %si
-       cmpw    $0, %si
-       je      2f
-       xorb    %bl, %bl
-       jmp     1b              /* retry */
-2:     
-       /* back to protected mode */
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %bl, %al        /* return value in %eax */
-       
-       popl    %esi
-       popl    %edi
-       popl    %ebx
-       popl    %ebp
-
-       ret     $(4 * 4)
-
-
-/*
- *   int grub_biosdisk_check_int13_extensions (int drive)
- *
- *   Check if LBA is supported for DRIVE. If it is supported, then
return
- *   the major version of extensions, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_check_int13_extensions)
-       pushl   %ebp
-       pushl   %ebx
-
-       /* drive */
-       movb    %al, %dl
-       /* enter real mode */
-       call    prot_to_real
-
-       .code16
-       movb    $0x41, %ah
-       movw    $0x55aa, %bx
-       int     $0x13           /* do the operation */
-       
-       /* check the result */
-       jc      1f
-       cmpw    $0xaa55, %bx
-       jne     1f
-
-       movb    %ah, %bl        /* save the major version into %bl */
-
-       /* check if AH=0x42 is supported */
-       andw    $1, %cx
-       jnz     2f
-       
-1:
-       xorb    %bl, %bl
-2:
-       /* back to protected mode */
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %bl, %al        /* return value in %eax */
-
-       popl    %ebx
-       popl    %ebp
-
-       ret
-
-
-/*
- *   int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void
*drp)
- *
- *   Return the geometry of DRIVE in a drive parameters, DRP. If an
error
- *   occurs, then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %esi
-
-       /* compute the address of drive parameters */
-       movw    %dx, %si
-       xorw    %dx, %dx
-       shrl    $4, %edx
-       movw    %dx, %bx        /* save the segment into %bx */
-       /* drive */
-       movb    %al, %dl
-       /* enter real mode */
-       call    prot_to_real
-
-       .code16
-       movb    $0x48, %ah
-       movw    %bx, %ds
-       int     $0x13           /* do the operation */
-       movb    %ah, %bl        /* save return value in %bl */
-       /* clear the data segment */
-       xorw    %ax, %ax
-       movw    %ax, %ds
-       /* back to protected mode */
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %bl, %al        /* return value in %eax */
-
-       popl    %esi
-       popl    %ebx
-       popl    %ebp
-       
-       ret
-
-
-/*
- *   int grub_biosdisk_get_diskinfo_standard (int drive,
- *                                            unsigned long *cylinders,

- *                                            unsigned long *heads,
- *                                            unsigned long *sectors)
- *
- *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If
an
- *   error occurs, then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_diskinfo_standard)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %edi
-
-       /* push CYLINDERS */
-       pushl   %edx
-       /* push HEADS */
-       pushl   %ecx
-       /* SECTORS is on the stack */
-       
-       /* drive */
-       movb    %al, %dl
-       /* enter real mode */
-       call    prot_to_real
-
-       .code16
-       movb    $0x8, %ah
-       int     $0x13           /* do the operation */
-       /* check if successful */
-       testb   %ah, %ah
-       jnz     1f
-       /* bogus BIOSes may not return an error number */
-       testb   $0x3f, %cl      /* 0 sectors means no disk */
-       jnz     1f              /* if non-zero, then succeed */
-       /* XXX 0x60 is one of the unused error numbers */
-       movb    $0x60, %ah
-1:
-       movb    %ah, %bl        /* save return value in %bl */
-       /* back to protected mode */
-       DATA32  call    real_to_prot
-       .code32
-
-       /* pop HEADS */
-       popl    %edi
-       movb    %dh, %al
-       incl    %eax    /* the number of heads is counted from zero */
-       movl    %eax, (%edi)
-
-       /* pop CYLINDERS */
-       popl    %edi
-       movb    %ch, %al
-       movb    %cl, %ah
-       shrb    $6, %ah /* the number of cylinders is counted from zero
*/
-       incl    %eax
-       movl    %eax, (%edi)
-
-       /* SECTORS */
-       movl    0x10(%esp), %edi
-       andb    $0x3f, %cl
-       movzbl  %cl, %eax
-       movl    %eax, (%edi)
-       
-       xorl    %eax, %eax
-       movb    %bl, %al        /* return value in %eax */
-
-       popl    %edi
-       popl    %ebx
-       popl    %ebp
-
-       ret     $4
-
-
-/*
- * int grub_biosdisk_get_num_floppies (void)
- */
-FUNCTION(grub_biosdisk_get_num_floppies)
-       pushl   %ebp
-
-       xorl    %edx, %edx
-       call    prot_to_real
-       
-       .code16
-       /* reset the disk system first */
-       int     $0x13
-1:
-       stc
-       
-       /* call GET DISK TYPE */
-       movb    $0x15, %ah
-       int     $0x13
-
-       jc      2f
-
-       /* check if this drive exists */        
-       testb   $0x3, %ah
-       jz      2f
-
-       incb    %dl
-       cmpb    $2, %dl
-       jne     1b
-2:
-       DATA32  call    real_to_prot
-       .code32
-
-       movl    %edx, %eax
-       popl    %ebp
-       ret
-       
-       
-/*
- *
- * grub_get_memsize(i) :  return the memory size in KB. i == 0 for
conventional
- *             memory, i == 1 for extended memory
- *     BIOS call "INT 12H" to get conventional memory size
- *     BIOS call "INT 15H, AH=88H" to get extended memory size
- *             Both have the return value in AX.
- *
- */
-
-FUNCTION(grub_get_memsize)
-       pushl   %ebp
-
-       movl    %eax, %edx
-
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       testl   %edx, %edx
-       jnz     xext
-
-       int     $0x12
-       jmp     xdone
-
-xext:
-       movb    $0x88, %ah
-       int     $0x15
-
-xdone:
-       movw    %ax, %dx
-
-       DATA32  call    real_to_prot
-       .code32
-
-       movw    %dx, %ax
-
-       popl    %ebp
-       ret
-
-
-/*
- *
- * grub_get_eisa_mmap() :  return packed EISA memory map, lower 16 bits
is
- *             memory between 1M and 16M in 1K parts, upper 16 bits is
- *             memory above 16M in 64K parts.  If error, return zero.
- *     BIOS call "INT 15H, AH=E801H" to get EISA memory map,
- *             AX = memory between 1M and 16M in 1K parts.
- *             BX = memory above 16M in 64K parts.
- *
- */
-
-FUNCTION(grub_get_eisa_mmap)
-       pushl   %ebp
-       pushl   %ebx
-
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       movw    $0xe801, %ax
-       int     $0x15
-
-       shll    $16, %ebx
-       movw    %ax, %bx
-
-       DATA32  call    real_to_prot
-       .code32
-
-       cmpb    $0x86, %bh
-       je      xnoteisa
-
-       movl    %ebx, %eax
-
-xnoteisa:
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- *
- * grub_get_mmap_entry(addr, cont) : address and old continuation value
(zero to
- *             start), for the Query System Address Map BIOS call.
- *
- *  Sets the first 4-byte int value of "addr" to the size returned by
- *  the call.  If the call fails, sets it to zero.
- *
- *     Returns:  new (non-zero) continuation value, 0 if done.
- */
-
-FUNCTION(grub_get_mmap_entry)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %edi
-       pushl   %esi
-
-       /* push ADDR */
-       pushl   %eax
-       
-       /* place address (+4) in ES:DI */
-       addl    $4, %eax
-       movl    %eax, %edi
-       andl    $0xf, %edi
-       shrl    $4, %eax
-       movl    %eax, %esi
-
-       /* set continuation value */
-       movl    %edx, %ebx
-
-       /* set default maximum buffer size */
-       movl    $0x14, %ecx
-
-       /* set EDX to 'SMAP' */
-       movl    $0x534d4150, %edx
-
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       movw    %si, %es
-       movl    $0xe820, %eax
-       int     $0x15
-
-       DATA32  jc      xnosmap
-
-       cmpl    $0x534d4150, %eax
-       jne     xnosmap
-
-       cmpl    $0x14, %ecx
-       jl      xnosmap
-
-       cmpl    $0x400, %ecx
-       jg      xnosmap
-
-       jmp     xsmap
-
-xnosmap:
-       xorl    %ecx, %ecx
-
-xsmap:
-       DATA32  call    real_to_prot
-       .code32
-
-       /* write length of buffer (zero if error) into ADDR */
-       popl    %eax
-       movl    %ecx, (%eax)
-
-       /* set return value to continuation */
-       movl    %ebx, %eax
-
-       popl    %esi
-       popl    %edi
-       popl    %ebx
-       popl    %ebp
-       ret
-
-       
-/*
- * void grub_console_real_putchar (int c)
- *
- * Put the character C on the console. Because GRUB wants to write a
- * character with an attribute, this implementation is a bit tricky.
- * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
- * (TELETYPE OUTPUT). Otherwise, save the original position, put a
space,
- * save the current position, restore the original position, write the
- * character and the attribute, and restore the current position.
- *
- * The reason why this is so complicated is that there is no easy way
to
- * get the height of the screen, and the TELETYPE OUPUT BIOS call
doesn't
- * support setting a background attribute.
- */
-FUNCTION(grub_console_real_putchar)
-       movl    %eax, %edx
-       pusha
-       movb    EXT_C(grub_console_cur_color), %bl
-       
-       call    prot_to_real
-       .code16
-       movb    %dl, %al
-       xorb    %bh, %bh
-
-       /* use teletype output if control character */
-       cmpb    $0x7, %al
-       je      1f
-       cmpb    $0x8, %al
-       je      1f
-       cmpb    $0xa, %al
-       je      1f
-       cmpb    $0xd, %al
-       je      1f
-
-       /* save the character and the attribute on the stack */
-       pushw   %ax
-       pushw   %bx
-       
-       /* get the current position */
-       movb    $0x3, %ah
-       int     $0x10
-
-       /* check the column with the width */
-       cmpb    $79, %dl
-       jl      2f
-       
-       /* print CR and LF, if next write will exceed the width */      
-       movw    $0x0e0d, %ax
-       int     $0x10
-       movb    $0x0a, %al
-       int     $0x10
-       
-       /* get the current position */
-       movb    $0x3, %ah
-       int     $0x10
-
-2:     
-       /* restore the character and the attribute */
-       popw    %bx
-       popw    %ax
-       
-       /* write the character with the attribute */
-       movb    $0x9, %ah
-       movw    $1, %cx
-       int     $0x10
-
-       /* move the cursor forward */
-       incb    %dl
-       movb    $0x2, %ah
-       int     $0x10
-
-       jmp     3f
-
-1:     movw    $1, %bx
-       movb    $0xe, %ah
-       int     $0x10
-       
-3:     DATA32  call    real_to_prot
-       .code32
-       
-       popa
-       ret
-       
-
-/*
- * int grub_console_getkey (void)
- * BIOS call "INT 16H Function 00H" to read character from keyboard
- *     Call with       %ah = 0x0
- *     Return:         %ah = keyboard scan code
- *                     %al = ASCII character
- */
-
-/* this table is used in translate_keycode below */
-translation_table:
-       .word   GRUB_CONSOLE_KEY_LEFT, 2
-       .word   GRUB_CONSOLE_KEY_RIGHT, 6
-       .word   GRUB_CONSOLE_KEY_UP, 16
-       .word   GRUB_CONSOLE_KEY_DOWN, 14
-       .word   GRUB_CONSOLE_KEY_HOME, 1
-       .word   GRUB_CONSOLE_KEY_END, 5
-       .word   GRUB_CONSOLE_KEY_DC, 4
-       .word   GRUB_CONSOLE_KEY_BACKSPACE, 8
-       .word   GRUB_CONSOLE_KEY_PPAGE, 7
-       .word   GRUB_CONSOLE_KEY_NPAGE, 3
-       .word   0
-       
-/*
- * translate_keycode translates the key code %dx to an ascii code.
- */
-       .code16
-
-translate_keycode:
-       pushw   %bx
-       pushw   %si
-       
-       movw    $ABS(translation_table), %si
-       
-1:     lodsw
-       /* check if this is the end */
-       testw   %ax, %ax
-       jz      2f
-       /* load the ascii code into %ax */
-       movw    %ax, %bx
-       lodsw
-       /* check if this matches the key code */
-       cmpw    %bx, %dx
-       jne     1b
-       /* translate %dx, if successful */
-       movw    %ax, %dx
-
-2:     popw    %si
-       popw    %bx
-       ret
-
-       .code32
-       
-FUNCTION(grub_console_getkey)
-       pushl   %ebp
-
-       call    prot_to_real
-       .code16
-
-       int     $0x16
-
-       movw    %ax, %dx                /* real_to_prot uses %eax */
-       call    translate_keycode
-               
-       DATA32  call    real_to_prot
-       .code32
-
-       movw    %dx, %ax
-
-       popl    %ebp
-       ret
-
-
-/*
- * int grub_console_checkkey (void)
- *     if there is a character pending, return it; otherwise return -1
- * BIOS call "INT 16H Function 01H" to check whether a character is
pending
- *     Call with       %ah = 0x1
- *     Return:
- *             If key waiting to be input:
- *                     %ah = keyboard scan code
- *                     %al = ASCII character
- *                     Zero flag = clear
- *             else
- *                     Zero flag = set
- */
-FUNCTION(grub_console_checkkey)
-       pushl   %ebp
-       xorl    %edx, %edx
-       
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       movb    $0x1, %ah
-       int     $0x16
-
-       jz      notpending
-       
-       movw    %ax, %dx
-       DATA32  jmp     pending
-
-notpending:
-       decl    %edx
-
-pending:
-       DATA32  call    real_to_prot
-       .code32
-
-       movl    %edx, %eax
-
-       popl    %ebp
-       ret
-
-       
-/*
- * grub_uint16_t grub_console_getxy (void)
- * BIOS call "INT 10H Function 03h" to get cursor position
- *     Call with       %ah = 0x03
- *                     %bh = page
- *      Returns         %ch = starting scan line
- *                      %cl = ending scan line
- *                      %dh = row (0 is top)
- *                      %dl = column (0 is left)
- */
-
-
-FUNCTION(grub_console_getxy)
-       pushl   %ebp
-       pushl   %ebx                    /* save EBX */
-
-       call    prot_to_real
-       .code16
-
-        xorb   %bh, %bh                /* set page to 0 */
-       movb    $0x3, %ah
-       int     $0x10                   /* get cursor position */
-
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %dl, %ah
-       movb    %dh, %al
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-
-/*
- * void grub_console_gotoxy(grub_uint8_t x, grub_uint8_t y)
- * BIOS call "INT 10H Function 02h" to set cursor position
- *     Call with       %ah = 0x02
- *                     %bh = page
- *                      %dh = row (0 is top)
- *                      %dl = column (0 is left)
- */
-
-
-FUNCTION(grub_console_gotoxy)
-       pushl   %ebp
-       pushl   %ebx                    /* save EBX */
-
-       movb    %dl, %dh        /* %dh = y */
-       movb    %al, %dl        /* %dl = x */
-
-       call    prot_to_real
-       .code16
-
-        xorb   %bh, %bh                /* set page to 0 */
-       movb    $0x2, %ah
-       int     $0x10                   /* set cursor position */
-
-       DATA32  call    real_to_prot
-       .code32
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-       
-/*
- * void grub_console_cls (void)
- * BIOS call "INT 10H Function 09h" to write character and attribute
- *     Call with       %ah = 0x09
- *                      %al = (character)
- *                      %bh = (page number)
- *                      %bl = (attribute)
- *                      %cx = (number of times)
- */
-
-FUNCTION(grub_console_cls)
-       pushl   %ebp
-       pushl   %ebx                    /* save EBX */
-
-       call    prot_to_real
-       .code16
-
-       /* move the cursor to the beginning */
-       movb    $0x02, %ah
-       xorb    %bh, %bh
-       xorw    %dx, %dx
-       int     $0x10
-
-       /* write spaces to the entire screen */
-       movw    $0x0920, %ax
-       movw    $0x07, %bx
-       movw    $(80 * 25), %cx
-        int    $0x10
-
-       /* move back the cursor */
-       movb    $0x02, %ah
-       int     $0x10
-
-       DATA32  call    real_to_prot
-       .code32
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-       
-/*
- * void grub_console_setcursor (int on)
- * BIOS call "INT 10H Function 01h" to set cursor type
- *      Call with       %ah = 0x01
- *                      %ch = cursor starting scanline
- *                      %cl = cursor ending scanline
- */
-
-console_cursor_state:
-       .byte   1
-console_cursor_shape:
-       .word   0
-       
-FUNCTION(grub_console_setcursor)
-       pushl   %ebp
-       pushl   %ebx
-
-       /* push ON */
-       pushl   %eax
-       
-       /* check if the standard cursor shape has already been saved */
-       movw    console_cursor_shape, %ax
-       testw   %ax, %ax
-       jne     1f
-
-       call    prot_to_real
-       .code16
-
-       movb    $0x03, %ah
-       xorb    %bh, %bh
-       int     $0x10
-
-       DATA32  call    real_to_prot
-       .code32
-
-       movw    %cx, console_cursor_shape
-1:
-       /* set %cx to the designated cursor shape */
-       movw    $0x2000, %cx
-       popl    %eax
-       testl   %eax, %eax
-       jz      2f
-       movw    console_cursor_shape, %cx
-2:     
-       call    prot_to_real
-       .code16
-
-       movb    $0x1, %ah
-       int     $0x10 
-
-       DATA32  call    real_to_prot
-       .code32
-
-       popl    %ebx
-       popl    %ebp
-       ret
-               
-/*
- * grub_getrtsecs()
- *     if a seconds value can be read, read it and return it (BCD),
- *      otherwise return 0xFF
- * BIOS call "INT 1AH Function 02H" to check whether a character is
pending
- *     Call with       %ah = 0x2
- *     Return:
- *             If RT Clock can give correct values
- *                     %ch = hour (BCD)
- *                     %cl = minutes (BCD)
- *                      %dh = seconds (BCD)
- *                      %dl = daylight savings time (00h std, 01h
daylight)
- *                     Carry flag = clear
- *             else
- *                     Carry flag = set
- *                         (this indicates that the clock is updating,
or
- *                          that it isn't running)
- */
-FUNCTION(grub_getrtsecs)
-       pushl   %ebp
-
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       clc
-       movb    $0x2, %ah
-       int     $0x1a
-
-       DATA32  jnc     gottime
-       movb    $0xff, %dh
-
-gottime:
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %dh, %al
-
-       popl    %ebp
-       ret
-
-       
-/*
- * grub_get_rtc()
- *     return the real time in ticks, of which there are about
- *     18-20 per second
- */
-FUNCTION(grub_get_rtc)
-       pushl   %ebp
-
-       call    prot_to_real    /* enter real mode */
-       .code16
-
-       /* %ax is already zero */
-        int    $0x1a
-
-       DATA32  call    real_to_prot
-       .code32
-
-       movl    %ecx, %eax
-       shll    $16, %eax
-       movw    %dx, %ax
-
-       popl    %ebp
-       ret
-
-
-/*
- * unsigned char grub_vga_set_mode (unsigned char mode)
- */
-FUNCTION(grub_vga_set_mode)
-       pushl   %ebp
-       pushl   %ebx
-       movl    %eax, %ecx
-
-       call    prot_to_real
-       .code16
-       /* get current mode */
-       xorw    %bx, %bx
-       movb    $0x0f, %ah
-       int     $0x10
-       movb    %al, %dl
-
-       /* set the new mode */
-       movb    %cl, %al
-       xorb    %ah, %ah
-       int     $0x10
-
-       DATA32  call    real_to_prot
-       .code32
-
-       movb    %dl, %al
-       popl    %ebx
-       popl    %ebp
-       ret
-
-
-/*
- * unsigned char *grub_vga_get_font (void)
- */
-FUNCTION(grub_vga_get_font)
-       pushl   %ebp
-       pushl   %ebx
-
-       call    prot_to_real
-       .code16
-       movw    $0x1130, %ax
-       movb    $0x06, %bh
-       int     $0x10
-       movw    %es, %bx
-       movw    %bp, %dx
-       DATA32  call    real_to_prot
-       .code32
-
-       movzwl  %bx, %ecx
-       shll    $4, %ecx
-       movw    %dx, %ax
-       addl    %ecx, %eax
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_controller_info (struct
grub_vbe_info_block *controller_info)
- *
- * Register allocations for parameters:
- * %eax                *controller_info
- */
-FUNCTION(grub_vbe_get_controller_info)
-       pushl   %ebp
-       pushl   %edi
-
-       movw    %ax, %di        /* Store *controller_info to %edx:%di.
*/
-       xorw    %ax, %ax
-       shrl    $4, %eax
-       mov     %eax, %edx      /* prot_to_real destroys %eax.  */
-       
-       call    prot_to_real
-       .code16
-
-       pushw   %es
-
-       movw    %dx, %es        /* *controller_info is now on %es:%di.
*/
-       movw    $0x4f00, %ax
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       popw    %es
-
-       DATA32 call     real_to_prot
-       .code32
-
-       movl    %edx, %eax
-       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
-       
-       popl    %edi
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_mode_info (grub_uint32_t mode,
- *                                          struct
grub_vbe_mode_info_block *mode_info)
- *
- * Register allocations for parameters:
- * %eax                mode
- * %edx                *mode_info
- */
-FUNCTION(grub_vbe_get_mode_info)
-       pushl   %ebp
-       pushl   %edi
-
-       movl    %eax, %ecx      /* Store mode number to %ecx.  */
-
-       movw    %dx, %di        /* Store *mode_info to %edx:%di.  */
-       xorw    %dx, %dx
-       shrl    $4, %edx
-
-       call    prot_to_real
-       .code16
-
-       pushw   %es
-
-       movw    %dx, %es        /* *mode_info is now on %es:%di.  */
-       movw    $0x4f01, %ax
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       popw    %es
-
-       DATA32 call     real_to_prot
-       .code32
-
-       movl    %edx, %eax
-       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
-
-       popl    %edi
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_set_mode (grub_uint32_t mode,
- *                                     struct grub_vbe_crtc_info_block
*crtc_info)
- *
- * Register allocations for parameters:
- * %eax                mode
- * %edx                *crtc_info
- */
-FUNCTION(grub_vbe_set_mode)
-       pushl   %ebp
-       pushl   %ebx
-
-       movl    %eax, %ebx      /* Store mode in %ebx.  */
-
-       movw    %dx, %di        /* Store *crtc_info to %edx:%di.  */
-       xorw    %dx, %dx
-       shrl    $4, %edx
-
-       call    prot_to_real
-       .code16
-
-       pushw   %es
-
-       movw    %dx, %es        /* *crtc_info is now on %es:%di.  */
-
-       movw    $0x4f02, %ax
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       popw    %es
-
-       DATA32 call     real_to_prot
-       .code32
-       
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_mode (grub_uint32_t *mode)
- *
- * Register allocations for parameters:
- * %eax                *mode
- */
-FUNCTION(grub_vbe_get_mode)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %eax            /* Push *mode to stack.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f03, %ax
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-
-       popl    %edi            /* Pops *mode from stack to %edi.  */
-       andl    $0xFFFF, %ebx
-       movl    %ebx, (%edi)
-
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_set_memory_window (grub_uint32_t window,
- *                                              grub_uint32_t
position);
- *
- * Register allocations for parameters:
- * %eax                window
- * %edx                position
- */
-FUNCTION(grub_vbe_set_memory_window)
-       pushl   %ebp
-       pushl   %ebx
-
-       movl    %eax, %ebx
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f05, %ax
-       andw    $0x00ff, %bx    /* BL = window, BH = 0, Set memory
window.  */
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-       
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_memory_window (grub_uint32_t window,
- *                                              grub_uint32_t
*position);
- *
- * Register allocations for parameters:
- * %eax                window
- * %edx                *position
- */
-FUNCTION(grub_vbe_get_memory_window)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %edx            /* Push *position to stack.  */
-
-       movl    %eax, %ebx      /* Store window in %ebx.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f05, %ax
-       andw    $0x00ff, %bx    /* BL = window.  */
-       orw     $0x0100, %bx    /* BH = 1, Get memory window.  */
-       int     $0x10
-
-       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-
-       popl    %edi            /* pops *position from stack to %edi.
*/
-       andl    $0xFFFF, %edx
-       movl    %edx, (%edi)    /* Return position to caller.  */
-
-       movw    %bx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_set_scanline_length (grub_uint32_t
length)
- *
- * Register allocations for parameters:
- * %eax                length
- */
-FUNCTION(grub_vbe_set_scanline_length)
-       pushl   %ebp
-       pushl   %ebx
-
-       movl    %eax, %ecx      /* Store length in %ecx.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f06, %ax
-       movw    $0x0002, %bx    /* BL = 2, Set Scan Line in Bytes.  */
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-       
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_scanline_length (grub_uint32_t
*length)
- *
- * Register allocations for parameters:
- * %eax                *length
- */
-FUNCTION(grub_vbe_get_scanline_length)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %edx            /* Push *length to stack.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f06, %ax
-       movw    $0x0001, %bx    /* BL = 1, Get Scan Line Length (in
bytes).  */
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-
-       popl    %edi            /* Pops *length from stack to %edi.  */
-       andl    $0xFFFF, %ebx
-       movl    %ebx, (%edi)    /* Return length to caller.  */
-
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_set_display_start (grub_uint32_t x,
- *                                              grub_uint32_t y)
- *
- * Register allocations for parameters:
- * %eax                x
- * %edx                y
- */
-FUNCTION(grub_vbe_set_display_start)
-       pushl   %ebp
-       pushl   %ebx
-
-       movl    %eax, %ecx      /* Store x in %ecx.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f07, %ax
-       movw    $0x0080, %bx    /* BL = 80h, Set Display Start 
-                                  during Vertical Retrace.  */
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-       
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_get_display_start (grub_uint32_t *x,
- *                                              grub_uint32_t *y)
- *
- * Register allocations for parameters:
- * %eax                *x
- * %edx                *y
- */
-FUNCTION(grub_vbe_get_display_start)
-       pushl   %ebp
-       pushl   %ebx
-       pushl   %eax            /* Push *x to stack.  */
-       pushl   %edx            /* Push *y to stack.  */
-
-       call    prot_to_real
-       .code16
-
-       movw    $0x4f07, %ax
-       movw    $0x0001, %bx    /* BL = 1, Get Display Start.  */
-       int     $0x10
-
-       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
-
-       DATA32 call     real_to_prot
-       .code32
-
-       popl    %edi            /* Pops *y from stack to %edi.  */
-       andl    $0xFFFF, %edx
-       movl    %edx, (%edi)    /* Return y-position to caller.  */
-
-       popl    %edi            /* Pops *x from stack to %edi.  */
-       andl    $0xFFFF, %ecx
-       movl    %ecx, (%edi)    /* Return x-position to caller.  */
-
-       movw    %bx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret
-
-/*
- * grub_vbe_status_t grub_vbe_set_palette_data (grub_uint32_t
color_count,
- *                                             grub_uint32_t
start_index,
- *                                             struct
grub_vbe_palette_data *palette_data)
- *
- * Register allocations for parameters:
- * %eax                color_count
- * %edx                start_index
- * %ecx                *palette_data
- */
-FUNCTION(grub_vbe_set_palette_data)
-       pushl   %ebp
-       pushl   %ebx
-
-       movl    %eax, %ebx      /* Store color_count in %ebx.  */
-
-       movw    %cx, %di        /* Store *palette_data to %ecx:%di.  */
-       xorw    %cx, %cx
-       shrl    $4, %ecx
-
-       call    prot_to_real
-       .code16
-
-       pushw   %es
-
-       movw    %cx, %es        /* *palette_data is now on %es:%di.  */
-       movw    %bx, %cx        /* color_count is now on %cx.  */
-
-       movw    $0x4f09, %ax
-       xorw    %bx, %bx        /* BL = 0, Set Palette Data.  */
-       int     $0x10
-
-       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
-
-       popw    %es
-
-       DATA32 call     real_to_prot
-       .code32
-       
-       movw    %dx, %ax
-       andl    $0xFFFF, %eax   /* Return value in %eax.  */
-
-       popl    %ebx
-       popl    %ebp
-       ret


FILE kern/i386/pc/startup.S.memparm (NEW)

        (1) Same as old startup.S file, except parameters are passed in
stack 
            for global functions.

diff -EbwBruN ./kern/i386/pc/startup.S.memparm
../../src/grub-1.93/kern/i386/pc/startup.S.memparm
--- ./kern/i386/pc/startup.S.memparm    1970-01-01 01:00:00.000000000
+0100
+++ ../../src/grub-1.93/kern/i386/pc/startup.S.memparm  2006-05-04
00:04:26.000000000 +0200
@@ -0,0 +1,2209 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2005 Free Software
Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or
modify
+ *  it under the terms of the GNU General Public License as published
by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ * Note: These functions defined in this file may be called from C.
+ *       Be careful of that you must not modify some registers. Quote
+ *       from gcc-2.95.2/gcc/config/i386/i386.h:
+       
+   1 for registers not available across function calls.
+   These must include the FIXED_REGISTERS and also any
+   registers that can be used without being saved.
+   The latter must include the registers where values are returned
+   and the register where structure-value addresses are passed.
+   Aside from that, you can include as many other registers as you
like.
+
+  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
+{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
+ */
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ *       So the first three arguments are passed in %eax, %edx, and
%ecx,
+ *       respectively, and if a function has a fixed number of
arguments
+ *       and the number if greater than three, the function must return
+ *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+#include <grub/machine/boot.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/console.h>
+#include <grub/machine/linux.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/multiboot.h>
+               
+#define ABS(x) ((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR +
0x200)
+       
+       .file   "startup.S"
+
+       .text
+
+       /* Tell GAS to generate 16-bit instructions so that this code
works
+          in real mode. */
+       .code16
+
+       .globl  start, _start
+start:
+_start:
+       /*
+        *  Guarantee that "main" is loaded at 0x0:0x8200.
+        */
+       ljmp $0, $ABS(codestart)
+
+       /*
+        *  Compatibility version number
+        *
+        *  These MUST be at byte offset 6 and 7 of the executable
+        *  DO NOT MOVE !!!
+        */
+       . = EXT_C(start) + 0x6
+       .byte   GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+
+       /*
+        *  This is a special data area 8 bytes from the beginning.
+        */
+
+       . = EXT_C(start) + 0x8
+
+VARIABLE(grub_total_module_size)
+       .long   0
+VARIABLE(grub_kernel_image_size)
+       .long   0
+VARIABLE(grub_compressed_size)
+       .long   0
+VARIABLE(grub_install_dos_part)
+       .long   0xFFFFFFFF
+VARIABLE(grub_install_bsd_part)
+       .long   0xFFFFFFFF
+VARIABLE(grub_prefix)
+       .string "/boot/grub"
+
+       /*
+        *  Leave some breathing room for the prefix.
+        */
+
+       . = EXT_C(start) + 0x50
+
+/*
+ * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB
itself).
+ * This uses the a.out kludge to load raw binary to the area starting
at 1MB,
+ * and relocates itself after loaded.
+ */
+multiboot_header:
+       /* magic */
+       .long   0x1BADB002
+       /* flags */
+       .long   (1 << 16)
+       /* checksum */
+       .long   -0x1BADB002 - (1 << 16)
+       /* header addr */
+       .long   multiboot_header - _start + 0x100000 + 0x200
+       /* load addr */
+       .long   0x100000
+       /* load end addr */
+       .long   0
+       /* bss end addr */
+       .long   0
+       /* entry addr */
+       .long   multiboot_entry - _start + 0x100000 + 0x200
+       
+multiboot_entry:
+       .code32
+       /* obtain the boot device */
+       movl    12(%ebx), %edx
+
+       /* relocate the code */
+       movl    $(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx
+       addl    EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200,
%ecx
+       movl    $0x100000, %esi
+       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi
+       cld
+       rep
+       movsb
+       /* jump to the real address */
+       movl    $multiboot_trampoline, %eax
+       jmp     *%eax
+       
+multiboot_trampoline:
+       /* fill the boot information */
+       movl    %edx, %eax
+       shrl    $8, %eax
+       xorl    %ebx, %ebx
+       cmpb    $0xFF, %ah
+       je      1f
+       movb    %ah, %bl
+       movl    %ebx, EXT_C(grub_install_dos_part)
+1:
+       cmpb    $0xFF, %al
+       je      2f
+       movb    %al, %bl
+       movl    %ebx, EXT_C(grub_install_bsd_part)
+2:
+       shrl    $24, %edx
+       /* enter the usual booting */
+       call    prot_to_real
+       .code16
+       
+/* the real mode code continues... */
+codestart:
+       cli             /* we're not safe here! */
+
+       /* set up %ds, %ss, and %es */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       movw    %ax, %ss
+       movw    %ax, %es
+
+       /* set up the real mode/BIOS stack */
+       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
+       movl    %ebp, %esp
+
+       sti             /* we're safe again */
+
+       /* save boot drive reference */
+       ADDR32  movb    %dl, EXT_C(grub_boot_drive)
+
+       /* reset disk system (%ah = 0) */
+       int     $0x13
+       
+       /* transition to protected mode */
+       DATA32  call real_to_prot
+
+       /* The ".code32" directive takes GAS out of 16-bit mode. */
+       .code32
+
+       incl    %eax
+       call    EXT_C(grub_gate_a20)
+       
+       /* decompress the compressed part and put the result at 1MB */
+       movl    $0x100000, %esi
+       movl    $(START_SYMBOL + GRUB_KERNEL_MACHINE_RAW_SIZE), %edi
+
+       pushl   %esi
+       pushl   EXT_C(grub_compressed_size)
+       pushl   %edi
+       call    lzo1x_decompress
+       addl    $12, %esp
+
+       /* copy back the decompressed part */
+       movl    %eax, %ecx
+       cld
+       rep
+       movsb
+
+       /* copy modules before cleaning out the bss */
+       movl    EXT_C(grub_total_module_size), %ecx
+       movl    EXT_C(grub_kernel_image_size), %esi
+       addl    %ecx, %esi
+       addl    $START_SYMBOL, %esi
+       decl    %esi
+       movl    $END_SYMBOL, %edi
+       addl    %ecx, %edi
+       decl    %edi
+       std
+       rep
+       movsb
+       
+       /* clean out the bss */
+       movl    $BSS_START_SYMBOL, %edi
+
+       /* compute the bss length */
+       movl    $END_SYMBOL, %ecx
+       subl    %edi, %ecx
+                       
+       /* clean out */
+       xorl    %eax, %eax
+       cld
+       rep
+       stosb
+       
+       /*
+        *  Call the start of main body of C code.
+        */
+       call EXT_C(grub_main)
+
+
+/*
+ *  This is the area for all of the special variables.
+ */
+
+       .p2align        2       /* force 4-byte alignment */
+
+protstack:
+       .long   GRUB_MEMORY_MACHINE_PROT_STACK
+
+VARIABLE(grub_boot_drive)
+       .long   0
+
+VARIABLE(grub_start_addr)
+       .long   START_SYMBOL
+
+VARIABLE(grub_end_addr)
+       .long   END_SYMBOL
+       
+VARIABLE(grub_apm_bios_info)
+       .word   0       /* version */
+       .word   0       /* cseg */
+       .long   0       /* offset */
+       .word   0       /* cseg_16 */
+       .word   0       /* dseg_16 */
+       .word   0       /* cseg_len */
+       .word   0       /* cseg_16_len */
+       .word   0       /* dseg_16_len */
+       
+/*
+ * This is the Global Descriptor Table
+ *
+ *  An entry, a "Segment Descriptor", looks like this:
+ *
+ * 31          24         19   16                 7           0
+ * ------------------------------------------------------------
+ * |             | |B| |A|       | |   |1|0|E|W|A|            |
+ * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |  4
+ * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
+ * ------------------------------------------------------------
+ * |                             |                            |
+ * |        BASE 15..0           |       LIMIT 15..0          |  0
+ * |                             |                            |
+ * ------------------------------------------------------------
+ *
+ *  Note the ordering of the data items is reversed from the above
+ *  description.
+ */
+
+       .p2align        2       /* force 4-byte alignment */
+gdt:
+       .word   0, 0
+       .byte   0, 0, 0, 0
+
+       /* -- code segment --
+        * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity),
present
+        * type = 32bit code execute/read, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x9A, 0xCF, 0
+
+       /* -- data segment --
+        * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+        * type = 32 bit data read/write, DPL = 0 
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x92, 0xCF, 0
+
+       /* -- 16 bit real mode CS --
+        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+        * type = 16 bit code execute/read only/conforming, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x9E, 0, 0
+
+       /* -- 16 bit real mode DS --
+        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+        * type = 16 bit data read/write, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x92, 0, 0
+
+
+/* this is the GDT descriptor */
+gdtdesc:
+       .word   0x27                    /* limit */
+       .long   gdt                     /* addr */
+
+       
+/*
+ *  These next two routines, "real_to_prot" and "prot_to_real" are
structured
+ *  in a very specific way.  Be very careful when changing them.
+ *
+ *  NOTE:  Use of either one messes up %eax and %ebp.
+ */
+
+real_to_prot:
+       .code16
+       cli
+
+       /* load the GDT register */
+       DATA32  ADDR32  lgdt    gdtdesc
+
+       /* turn on protected mode */
+       movl    %cr0, %eax
+       orl     $GRUB_MEMORY_MACHINE_CR0_PE_ON, %eax
+       movl    %eax, %cr0
+
+       /* jump to relocation, flush prefetch queue, and reload %cs */
+       DATA32  ljmp    $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
+
+       .code32
+protcseg:
+       /* reload other segment registers */
+       movw    $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* put the return address in a known safe location */
+       movl    (%esp), %eax
+       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+       /* get protected mode stack */
+       movl    protstack, %eax
+       movl    %eax, %esp
+       movl    %eax, %ebp
+
+       /* get return address onto the right stack */
+       movl    GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+       movl    %eax, (%esp)
+
+       /* zero %eax */
+       xorl    %eax, %eax
+
+       /* return on the old (or initialized) stack! */
+       ret
+
+
+prot_to_real:
+       /* just in case, set GDT */
+       lgdt    gdtdesc
+
+       /* save the protected mode stack */
+       movl    %esp, %eax
+       movl    %eax, protstack
+
+       /* get the return address */
+       movl    (%esp), %eax
+       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+       /* set up new stack */
+       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+       movl    %eax, %esp
+       movl    %eax, %ebp
+
+       /* set up segment limits */
+       movw    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* this might be an extra step */
+       /* jump to a 16 bit segment */
+       ljmp    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
+
+tmpcseg:
+       .code16
+
+       /* clear the PE bit of CR0 */
+       movl    %cr0, %eax
+       andl    $(~GRUB_MEMORY_MACHINE_CR0_PE_ON), %eax
+       movl    %eax, %cr0
+
+       /* flush prefetch queue, reload %cs */
+       DATA32  ljmp    $0, $realcseg
+
+realcseg:
+       /* we are in real mode now
+        * set up the real mode segment registers : DS, SS, ES
+        */
+       /* zero %eax */
+       xorl    %eax, %eax
+
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* restore interrupts */
+       sti
+
+       /* return on new stack! */
+       DATA32  ret
+
+       .code32
+
+
+/*
+ * grub_gate_a20(int on)
+ *
+ * Gate address-line 20 for high memory.
+ *
+ * This routine is probably overconservative in what it does, but so
what?
+ *
+ * It also eats any keystrokes in the keyboard buffer.  :-(
+ */
+
+FUNCTION(grub_gate_a20)
+       movl    %eax, %edx
+
+gate_a20_test_current_state:   
+       /* first of all, test if already in a good state */
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_bios
+       ret
+
+gate_a20_try_bios:
+       /* second, try a BIOS call */
+       pushl   %ebp
+       call    prot_to_real
+
+       .code16
+       movw    $0x2400, %ax
+       testb   %dl, %dl
+       jz      1f
+       incw    %ax
+1:     int     $0x15
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebp
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_keyboard_controller
+       ret
+       
+gate_a20_flush_keyboard_buffer:
+       inb     $0x64
+       andb    $0x02, %al
+       jnz     gate_a20_flush_keyboard_buffer
+2:     
+       inb     $0x64
+       andb    $0x01, %al
+       jz      3f
+       inb     $0x60
+       jmp     2b
+3:
+       ret
+       
+gate_a20_try_keyboard_controller:      
+       /* third, try the keyboard controller */
+       call    gate_a20_flush_keyboard_buffer
+
+       movb    $0xd1, %al
+       outb    $0x64
+4:     
+       inb     $0x64
+       andb    $0x02, %al
+       jnz     4b
+
+       movb    $0xdd, %al
+       testb   %dl, %dl
+       jz      5f
+       orb     $0x02, %al
+5:     outb    $0x60
+       call    gate_a20_flush_keyboard_buffer
+
+       /* output a dummy command (USB keyboard hack) */
+       movb    $0xff, %al
+       outb    $0x64
+       call    gate_a20_flush_keyboard_buffer
+
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_system_control_port_a
+       ret
+
+gate_a20_try_system_control_port_a:
+       /* fourth, try the system control port A */
+       inb     $0x92
+       andb    $(~0x03), %al
+       testb   %dl, %dl
+       jz      6f
+       orb     $0x02, %al
+6:     outb    $0x92
+
+       /* When turning off Gate A20, do not check the state strictly,
+          because a failure is not fatal usually, and Gate A20 is
always
+          on some modern machines.  */
+       testb   %dl, %dl
+       jz      7f      
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       /* everything failed, so restart from the beginning */
+       jnz     gate_a20_try_bios
+7:     ret
+       
+gate_a20_check_state:
+       /* iterate the checking for a while */
+       movl    $100, %ecx
+1:     
+       call    3f
+       cmpb    %al, %dl
+       jz      2f
+       loop    1b
+2:
+       ret
+3:     
+       pushl   %ebx
+       pushl   %ecx
+       xorl    %eax, %eax
+       /* compare the byte at 0x8000 with that at 0x108000 */
+       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx
+       pushl   %ebx
+       /* save the original byte in CL */
+       movb    (%ebx), %cl
+       /* store the value at 0x108000 in AL */
+       addl    $0x100000, %ebx
+       movb    (%ebx), %al
+       /* try to set one less value at 0x8000 */
+       popl    %ebx
+       movb    %al, %ch
+       decb    %ch
+       movb    %ch, (%ebx)
+       /* serialize */
+       outb    %al, $0x80
+       outb    %al, $0x80
+       /* obtain the value at 0x108000 in CH */
+       pushl   %ebx
+       addl    $0x100000, %ebx
+       movb    (%ebx), %ch
+       /* this result is 1 if A20 is on or 0 if it is off */
+       subb    %ch, %al
+       xorb    $1, %al
+       /* restore the original */
+       popl    %ebx
+       movb    %cl, (%ebx)
+       popl    %ecx
+       popl    %ebx
+       ret
+
+#include "lzo1x.S"
+
+
+/*
+ *  This call is special...  it never returns...  in fact it should
simply
+ *  hang at this point!
+ */
+
+FUNCTION(grub_stop)
+       call    prot_to_real
+
+       /*
+        * This next part is sort of evil.  It takes advantage of the
+        * byte ordering on the x86 to work in either 16-bit or 32-bit
+        * mode, so think about it before changing it.
+        */
+
+FUNCTION(grub_hard_stop)
+       hlt
+       jmp EXT_C(grub_hard_stop)
+
+
+/*
+ * grub_stop_floppy()
+ *
+ * Stop the floppy drive from spinning, so that other software is
+ * jumped to with a known state.
+ */
+FUNCTION(grub_stop_floppy)
+       movw    $0x3F2, %dx
+       xorb    %al, %al
+       outb    %al, %dx
+       ret
+
+/*
+ * grub_reboot()
+ *
+ * Reboot the system. At the moment, rely on BIOS.
+ */
+FUNCTION(grub_reboot)
+       call    prot_to_real
+       .code16
+       /* cold boot */
+       movw    $0x0472, %di
+       movw    %ax, (%di)
+       ljmp    $0xFFFF, $0x0000
+       .code32
+       
+/*
+ * grub_halt(int no_apm)
+ *
+ * Halt the system, using APM if possible. If NO_APM is true, don't use
+ * APM even if it is available.
+ */
+FUNCTION(grub_halt)
+       /* see if zero */
+       mov     4(%esp), %eax
+       testl   %eax, %eax
+       jnz     EXT_C(grub_stop)
+
+       call    prot_to_real
+       .code16
+       
+       /* detect APM */
+       movw    $0x5300, %ax
+       xorw    %bx, %bx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+       /* don't check %bx for buggy BIOSes... */
+
+       /* disconnect APM first */
+       movw    $0x5304, %ax
+       xorw    %bx, %bx
+       int     $0x15
+
+       /* connect APM */
+       movw    $0x5301, %ax
+       xorw    %bx, %bx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+
+       /* set APM protocol level - 1.1 or bust. (this covers APM 1.2
also) */
+       movw    $0x530E, %ax
+       xorw    %bx, %bx
+       movw    $0x0101, %cx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+       
+       /* set the power state to off */
+       movw    $0x5307, %ax
+       movw    $1, %bx
+       movw    $3, %cx
+       int     $0x15
+
+       /* shouldn't reach here */
+       jmp     EXT_C(grub_hard_stop)
+       .code32
+       
+       
+/*
+ *  void grub_chainloader_real_boot (int drive, void *part_addr)
+ *
+ *  This starts another boot loader.
+ */
+
+FUNCTION(grub_chainloader_real_boot)
+       pushl   %edx
+       pushl   %eax
+
+       call    EXT_C(grub_dl_unload_all)
+
+       /* set up to pass boot drive */
+       mov     4(%esp),%edx
+
+       /* ESI must point to a partition table entry */
+       mov     8(%esp),%esi
+
+       /* Turn off Gate A20 */
+       xorl    %eax, %eax
+       call    EXT_C(grub_gate_a20)
+       
+       call    prot_to_real
+       .code16
+       ljmp    $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
+       .code32
+
+
+/*
+ * void grub_linux_boot_zimage (void)
+ */
+VARIABLE(grub_linux_prot_size)
+       .long   0
+VARIABLE(grub_linux_tmp_addr)
+       .long   0
+VARIABLE(grub_linux_real_addr)
+       .long   0
+       
+FUNCTION(grub_linux_boot_zimage)
+       /* copy the kernel */
+       movl    EXT_C(grub_linux_prot_size), %ecx
+       addl    $3, %ecx
+       shrl    $2, %ecx
+       movl    $GRUB_LINUX_BZIMAGE_ADDR, %esi
+       movl    $GRUB_LINUX_ZIMAGE_ADDR, %edi
+       cld
+       rep
+       movsl
+
+FUNCTION(grub_linux_boot_bzimage)
+       call    EXT_C(grub_dl_unload_all)
+       
+       movl    EXT_C(grub_linux_real_addr), %ebx
+
+       /* copy the real mode code */
+       movl    EXT_C(grub_linux_tmp_addr), %esi
+       movl    %ebx, %edi
+       movl    $GRUB_LINUX_SETUP_MOVE_SIZE, %ecx
+       cld
+       rep
+       movsb
+
+       /* change %ebx to the segment address */
+       shrl    $4, %ebx
+       movl    %ebx, %eax
+       addl    $0x20, %eax
+       movw    %ax, linux_setup_seg
+
+       /* XXX new stack pointer in safe area for calling functions */
+       movl    $0x4000, %esp
+       call    EXT_C(grub_stop_floppy)
+
+       /* final setup for linux boot */
+       call    prot_to_real
+       .code16
+
+       cli
+       movw    %bx, %ss
+       movw    $GRUB_LINUX_SETUP_STACK, %sp
+
+       movw    %bx, %ds
+       movw    %bx, %es
+       movw    %bx, %fs
+       movw    %bx, %gs
+
+       /* ljmp */
+       .byte   0xea
+       .word   0
+linux_setup_seg:
+       .word   0
+       .code32
+
+               
+/*
+ * This starts the multiboot kernel.
+ */
+
+FUNCTION(grub_multiboot_real_boot)
+       /* Push the entry address on the stack.  */
+       /* Move the address of the multiboot information structure to
ebx.  */
+       movl    8(%esp),%ebx
+       
+       /* Unload all modules and stop the floppy driver.  */
+       call    EXT_C(grub_dl_unload_all)
+       call    EXT_C(grub_stop_floppy)
+
+       /* Interrupts should be disabled.  */
+       cli
+       
+       /* Move the magic value into eax and jump to the kernel.  */
+       movl    $GRUB_MB_MAGIC2,%eax
+       movl    4(%esp),%ecx
+       jmp     *%ecx
+       
+       
+/*
+ *   int grub_biosdisk_rw_int13_extensions (int ah, int drive, void
*dap)
+ *
+ *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
+ *   is passed for disk address packet. If an error occurs, return
+ *   non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_rw_int13_extensions)
+       movw    0x4(%esp), %ax
+       movw    0x8(%esp), %dx
+       mov     0xC(%esp), %ecx
+       pushl   %ebp
+       pushl   %esi
+
+       /* compute the address of disk_address_packet */
+       movw    %cx, %si
+       xorw    %cx, %cx
+       shrl    $4, %ecx        /* save the segment to cx */
+
+       /* ah */
+       movb    %al, %dh
+       /* enter real mode */
+       call    prot_to_real
+       
+       .code16
+       movb    %dh, %ah
+       movw    %cx, %ds
+       int     $0x13           /* do the operation */
+       movb    %ah, %dl        /* save return value */
+       /* clear the data segment */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %al        /* return value in %eax */
+
+       popl    %esi
+       popl    %ebp
+
+       ret
+       
+/*
+ *   int grub_biosdisk_rw_standard (int ah, int drive, int coff, int
hoff,
+ *                                  int soff, int nsec, int segment)
+ *
+ *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
+ *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
+ *   return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_rw_standard)
+       pushl   %ebp
+       movl    %esp, %ebp
+
+       pushl   %ebx
+       pushl   %edi
+       pushl   %esi
+
+       /* set up CHS information */
+       movw    0x8(%ebp), %ax
+       movw    0xC(%ebp), %dx
+       movw    0x10(%ebp), %cx
+
+       /* set %ch to low eight bits of cylinder */
+       xchgb   %cl, %ch
+       /* set bits 6-7 of %cl to high two bits of cylinder */
+       shlb    $6, %cl
+       /* set bits 0-5 of %cl to sector */
+       addb    0x18(%ebp), %cl
+       /* set %dh to head */
+       movb    0x14(%ebp), %dh
+       /* set %ah to AH */
+       movb    %al, %ah
+       /* set %al to NSEC */
+       movb    0x1c(%ebp), %al
+       /* save %ax in %di */
+       movw    %ax, %di
+       /* save SEGMENT in %bx */
+       movw    0x20(%ebp), %bx
+               
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movw    %bx, %es
+       xorw    %bx, %bx
+       movw    $3, %si         /* attempt at least three times */
+
+1:     
+       movw    %di, %ax
+       int     $0x13           /* do the operation */
+       jnc     2f              /* check if successful */
+
+       movb    %ah, %bl        /* save return value */
+       /* if fail, reset the disk system */
+       xorw    %ax, %ax
+       int     $0x13
+       
+       decw    %si
+       cmpw    $0, %si
+       je      2f
+       xorb    %bl, %bl
+       jmp     1b              /* retry */
+2:     
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+       
+       popl    %esi
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+
+       ret     $(4 * 4)
+
+
+/*
+ *   int grub_biosdisk_check_int13_extensions (int drive)
+ *
+ *   Check if LBA is supported for DRIVE. If it is supported, then
return
+ *   the major version of extensions, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_check_int13_extensions)
+       movw    0x4(%esp), %ax
+       pushl   %ebp
+       pushl   %ebx
+
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x41, %ah
+       movw    $0x55aa, %bx
+       int     $0x13           /* do the operation */
+       
+       /* check the result */
+       jc      1f
+       cmpw    $0xaa55, %bx
+       jne     1f
+
+       movb    %ah, %bl        /* save the major version into %bl */
+
+       /* check if AH=0x42 is supported */
+       andw    $1, %cx
+       jnz     2f
+       
+1:
+       xorb    %bl, %bl
+2:
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %ebx
+       popl    %ebp
+
+       ret
+
+
+/*
+ *   int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void
*drp)
+ *
+ *   Return the geometry of DRIVE in a drive parameters, DRP. If an
error
+ *   occurs, then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
+       movw    0x4(%esp), %ax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %esi
+
+       /* compute the address of drive parameters */
+       movw    %dx, %si
+       xorw    %dx, %dx
+       shrl    $4, %edx
+       movw    %dx, %bx        /* save the segment into %bx */
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x48, %ah
+       movw    %bx, %ds
+       int     $0x13           /* do the operation */
+       movb    %ah, %bl        /* save return value in %bl */
+       /* clear the data segment */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %esi
+       popl    %ebx
+       popl    %ebp
+       
+       ret
+
+
+/*
+ *   int grub_biosdisk_get_diskinfo_standard (int drive,
+ *                                            unsigned long *cylinders,

+ *                                            unsigned long *heads,
+ *                                            unsigned long *sectors)
+ *
+ *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If
an
+ *   error occurs, then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_diskinfo_standard)
+       movw    0x4(%esp), %ax
+       mov     0x8(%esp), %edx
+       mov     0xC(%esp), %ecx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+
+       /* push CYLINDERS */
+       pushl   %edx
+       /* push HEADS */
+       pushl   %ecx
+       /* SECTORS is on the stack */
+       
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x8, %ah
+       int     $0x13           /* do the operation */
+       /* check if successful */
+       testb   %ah, %ah
+       jnz     1f
+       /* bogus BIOSes may not return an error number */
+       testb   $0x3f, %cl      /* 0 sectors means no disk */
+       jnz     1f              /* if non-zero, then succeed */
+       /* XXX 0x60 is one of the unused error numbers */
+       movb    $0x60, %ah
+1:
+       movb    %ah, %bl        /* save return value in %bl */
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       /* pop HEADS */
+       popl    %edi
+       movb    %dh, %al
+       incl    %eax    /* the number of heads is counted from zero */
+       movl    %eax, (%edi)
+
+       /* pop CYLINDERS */
+       popl    %edi
+       movb    %ch, %al
+       movb    %cl, %ah
+       shrb    $6, %ah /* the number of cylinders is counted from zero
*/
+       incl    %eax
+       movl    %eax, (%edi)
+
+       /* SECTORS */
+       movl    0x1c(%esp), %edi
+       andb    $0x3f, %cl
+       movzbl  %cl, %eax
+       movl    %eax, (%edi)
+       
+       xorl    %eax, %eax
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+
+       ret     $4
+
+
+/*
+ * int grub_biosdisk_get_num_floppies (void)
+ */
+FUNCTION(grub_biosdisk_get_num_floppies)
+       pushl   %ebp
+
+       xorl    %edx, %edx
+       call    prot_to_real
+       
+       .code16
+       /* reset the disk system first */
+       int     $0x13
+1:
+       stc
+       
+       /* call GET DISK TYPE */
+       movb    $0x15, %ah
+       int     $0x13
+
+       jc      2f
+
+       /* check if this drive exists */        
+       testb   $0x3, %ah
+       jz      2f
+
+       incb    %dl
+       cmpb    $2, %dl
+       jne     1b
+2:
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       popl    %ebp
+       ret
+       
+       
+/*
+ *
+ * grub_get_memsize(i) :  return the memory size in KB. i == 0 for
conventional
+ *             memory, i == 1 for extended memory
+ *     BIOS call "INT 12H" to get conventional memory size
+ *     BIOS call "INT 15H, AH=88H" to get extended memory size
+ *             Both have the return value in AX.
+ *
+ */
+
+FUNCTION(grub_get_memsize)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+
+       movl    %eax, %edx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       testl   %edx, %edx
+       jnz     xext
+
+       int     $0x12
+       jmp     xdone
+
+xext:
+       movb    $0x88, %ah
+       int     $0x15
+
+xdone:
+       movw    %ax, %dx
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ *
+ * grub_get_eisa_mmap() :  return packed EISA memory map, lower 16 bits
is
+ *             memory between 1M and 16M in 1K parts, upper 16 bits is
+ *             memory above 16M in 64K parts.  If error, return zero.
+ *     BIOS call "INT 15H, AH=E801H" to get EISA memory map,
+ *             AX = memory between 1M and 16M in 1K parts.
+ *             BX = memory above 16M in 64K parts.
+ *
+ */
+
+FUNCTION(grub_get_eisa_mmap)
+       pushl   %ebp
+       pushl   %ebx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movw    $0xe801, %ax
+       int     $0x15
+
+       shll    $16, %ebx
+       movw    %ax, %bx
+
+       DATA32  call    real_to_prot
+       .code32
+
+       cmpb    $0x86, %bh
+       je      xnoteisa
+
+       movl    %ebx, %eax
+
+xnoteisa:
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ *
+ * grub_get_mmap_entry(addr, cont) : address and old continuation value
(zero to
+ *             start), for the Query System Address Map BIOS call.
+ *
+ *  Sets the first 4-byte int value of "addr" to the size returned by
+ *  the call.  If the call fails, sets it to zero.
+ *
+ *     Returns:  new (non-zero) continuation value, 0 if done.
+ */
+
+FUNCTION(grub_get_mmap_entry)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       pushl   %esi
+
+       /* push ADDR */
+       pushl   %eax
+       
+       /* place address (+4) in ES:DI */
+       addl    $4, %eax
+       movl    %eax, %edi
+       andl    $0xf, %edi
+       shrl    $4, %eax
+       movl    %eax, %esi
+
+       /* set continuation value */
+       movl    %edx, %ebx
+
+       /* set default maximum buffer size */
+       movl    $0x14, %ecx
+
+       /* set EDX to 'SMAP' */
+       movl    $0x534d4150, %edx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movw    %si, %es
+       movl    $0xe820, %eax
+       int     $0x15
+
+       DATA32  jc      xnosmap
+
+       cmpl    $0x534d4150, %eax
+       jne     xnosmap
+
+       cmpl    $0x14, %ecx
+       jl      xnosmap
+
+       cmpl    $0x400, %ecx
+       jg      xnosmap
+
+       jmp     xsmap
+
+xnosmap:
+       xorl    %ecx, %ecx
+
+xsmap:
+       DATA32  call    real_to_prot
+       .code32
+
+       /* write length of buffer (zero if error) into ADDR */
+       popl    %eax
+       movl    %ecx, (%eax)
+
+       /* set return value to continuation */
+       movl    %ebx, %eax
+
+       popl    %esi
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_real_putchar (int c)
+ *
+ * Put the character C on the console. Because GRUB wants to write a
+ * character with an attribute, this implementation is a bit tricky.
+ * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
+ * (TELETYPE OUTPUT). Otherwise, save the original position, put a
space,
+ * save the current position, restore the original position, write the
+ * character and the attribute, and restore the current position.
+ *
+ * The reason why this is so complicated is that there is no easy way
to
+ * get the height of the screen, and the TELETYPE OUPUT BIOS call
doesn't
+ * support setting a background attribute.
+ */
+FUNCTION(grub_console_real_putchar)
+       mov     0x4(%esp), %eax
+       movl    %eax, %edx
+       pusha
+       movb    EXT_C(grub_console_cur_color), %bl
+       
+       call    prot_to_real
+       .code16
+       movb    %dl, %al
+       xorb    %bh, %bh
+
+       /* use teletype output if control character */
+       cmpb    $0x7, %al
+       je      1f
+       cmpb    $0x8, %al
+       je      1f
+       cmpb    $0xa, %al
+       je      1f
+       cmpb    $0xd, %al
+       je      1f
+
+       /* save the character and the attribute on the stack */
+       pushw   %ax
+       pushw   %bx
+       
+       /* get the current position */
+       movb    $0x3, %ah
+       int     $0x10
+
+       /* check the column with the width */
+       cmpb    $79, %dl
+       jl      2f
+       
+       /* print CR and LF, if next write will exceed the width */      
+       movw    $0x0e0d, %ax
+       int     $0x10
+       movb    $0x0a, %al
+       int     $0x10
+       
+       /* get the current position */
+       movb    $0x3, %ah
+       int     $0x10
+
+2:     
+       /* restore the character and the attribute */
+       popw    %bx
+       popw    %ax
+       
+       /* write the character with the attribute */
+       movb    $0x9, %ah
+       movw    $1, %cx
+       int     $0x10
+
+       /* move the cursor forward */
+       incb    %dl
+       movb    $0x2, %ah
+       int     $0x10
+
+       jmp     3f
+
+1:     movw    $1, %bx
+       movb    $0xe, %ah
+       int     $0x10
+       
+3:     DATA32  call    real_to_prot
+       .code32
+       
+       popa
+       ret
+       
+
+/*
+ * int grub_console_getkey (void)
+ * BIOS call "INT 16H Function 00H" to read character from keyboard
+ *     Call with       %ah = 0x0
+ *     Return:         %ah = keyboard scan code
+ *                     %al = ASCII character
+ */
+
+/* this table is used in translate_keycode below */
+translation_table:
+       .word   GRUB_CONSOLE_KEY_LEFT, 2
+       .word   GRUB_CONSOLE_KEY_RIGHT, 6
+       .word   GRUB_CONSOLE_KEY_UP, 16
+       .word   GRUB_CONSOLE_KEY_DOWN, 14
+       .word   GRUB_CONSOLE_KEY_HOME, 1
+       .word   GRUB_CONSOLE_KEY_END, 5
+       .word   GRUB_CONSOLE_KEY_DC, 4
+       .word   GRUB_CONSOLE_KEY_BACKSPACE, 8
+       .word   GRUB_CONSOLE_KEY_PPAGE, 7
+       .word   GRUB_CONSOLE_KEY_NPAGE, 3
+       .word   0
+       
+/*
+ * translate_keycode translates the key code %dx to an ascii code.
+ */
+       .code16
+
+translate_keycode:
+       pushw   %bx
+       pushw   %si
+       
+       movw    $ABS(translation_table), %si
+       
+1:     lodsw
+       /* check if this is the end */
+       testw   %ax, %ax
+       jz      2f
+       /* load the ascii code into %ax */
+       movw    %ax, %bx
+       lodsw
+       /* check if this matches the key code */
+       cmpw    %bx, %dx
+       jne     1b
+       /* translate %dx, if successful */
+       movw    %ax, %dx
+
+2:     popw    %si
+       popw    %bx
+       ret
+
+       .code32
+       
+FUNCTION(grub_console_getkey)
+       pushl   %ebp
+
+       call    prot_to_real
+       .code16
+
+       int     $0x16
+
+       movw    %ax, %dx                /* real_to_prot uses %eax */
+       call    translate_keycode
+               
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ * int grub_console_checkkey (void)
+ *     if there is a character pending, return it; otherwise return -1
+ * BIOS call "INT 16H Function 01H" to check whether a character is
pending
+ *     Call with       %ah = 0x1
+ *     Return:
+ *             If key waiting to be input:
+ *                     %ah = keyboard scan code
+ *                     %al = ASCII character
+ *                     Zero flag = clear
+ *             else
+ *                     Zero flag = set
+ */
+FUNCTION(grub_console_checkkey)
+       pushl   %ebp
+       xorl    %edx, %edx
+       
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movb    $0x1, %ah
+       int     $0x16
+
+       jz      notpending
+       
+       movw    %ax, %dx
+       DATA32  jmp     pending
+
+notpending:
+       decl    %edx
+
+pending:
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %edx, %eax
+
+       popl    %ebp
+       ret
+
+       
+/*
+ * grub_uint16_t grub_console_getxy (void)
+ * BIOS call "INT 10H Function 03h" to get cursor position
+ *     Call with       %ah = 0x03
+ *                     %bh = page
+ *      Returns         %ch = starting scan line
+ *                      %cl = ending scan line
+ *                      %dh = row (0 is top)
+ *                      %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_getxy)
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       call    prot_to_real
+       .code16
+
+        xorb   %bh, %bh                /* set page to 0 */
+       movb    $0x3, %ah
+       int     $0x10                   /* get cursor position */
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %ah
+       movb    %dh, %al
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+
+/*
+ * void grub_console_gotoxy(grub_uint8_t x, grub_uint8_t y)
+ * BIOS call "INT 10H Function 02h" to set cursor position
+ *     Call with       %ah = 0x02
+ *                     %bh = page
+ *                      %dh = row (0 is top)
+ *                      %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_gotoxy)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       movb    %dl, %dh        /* %dh = y */
+       movb    %al, %dl        /* %dl = x */
+
+       call    prot_to_real
+       .code16
+
+        xorb   %bh, %bh                /* set page to 0 */
+       movb    $0x2, %ah
+       int     $0x10                   /* set cursor position */
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_cls (void)
+ * BIOS call "INT 10H Function 09h" to write character and attribute
+ *     Call with       %ah = 0x09
+ *                      %al = (character)
+ *                      %bh = (page number)
+ *                      %bl = (attribute)
+ *                      %cx = (number of times)
+ */
+
+FUNCTION(grub_console_cls)
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       call    prot_to_real
+       .code16
+
+       /* move the cursor to the beginning */
+       movb    $0x02, %ah
+       xorb    %bh, %bh
+       xorw    %dx, %dx
+       int     $0x10
+
+       /* write spaces to the entire screen */
+       movw    $0x0920, %ax
+       movw    $0x07, %bx
+       movw    $(80 * 25), %cx
+        int    $0x10
+
+       /* move back the cursor */
+       movb    $0x02, %ah
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_setcursor (int on)
+ * BIOS call "INT 10H Function 01h" to set cursor type
+ *      Call with       %ah = 0x01
+ *                      %ch = cursor starting scanline
+ *                      %cl = cursor ending scanline
+ */
+
+console_cursor_state:
+       .byte   1
+console_cursor_shape:
+       .word   0
+       
+FUNCTION(grub_console_setcursor)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %ebx
+
+       /* push ON */
+       pushl   %eax
+       
+       /* check if the standard cursor shape has already been saved */
+       movw    console_cursor_shape, %ax
+       testw   %ax, %ax
+       jne     1f
+
+       call    prot_to_real
+       .code16
+
+       movb    $0x03, %ah
+       xorb    %bh, %bh
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %cx, console_cursor_shape
+1:
+       /* set %cx to the designated cursor shape */
+       movw    $0x2000, %cx
+       popl    %eax
+       testl   %eax, %eax
+       jz      2f
+       movw    console_cursor_shape, %cx
+2:     
+       call    prot_to_real
+       .code16
+
+       movb    $0x1, %ah
+       int     $0x10 
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+               
+/*
+ * grub_getrtsecs()
+ *     if a seconds value can be read, read it and return it (BCD),
+ *      otherwise return 0xFF
+ * BIOS call "INT 1AH Function 02H" to check whether a character is
pending
+ *     Call with       %ah = 0x2
+ *     Return:
+ *             If RT Clock can give correct values
+ *                     %ch = hour (BCD)
+ *                     %cl = minutes (BCD)
+ *                      %dh = seconds (BCD)
+ *                      %dl = daylight savings time (00h std, 01h
daylight)
+ *                     Carry flag = clear
+ *             else
+ *                     Carry flag = set
+ *                         (this indicates that the clock is updating,
or
+ *                          that it isn't running)
+ */
+FUNCTION(grub_getrtsecs)
+       pushl   %ebp
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       clc
+       movb    $0x2, %ah
+       int     $0x1a
+
+       DATA32  jnc     gottime
+       movb    $0xff, %dh
+
+gottime:
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dh, %al
+
+       popl    %ebp
+       ret
+
+       
+/*
+ * grub_get_rtc()
+ *     return the real time in ticks, of which there are about
+ *     18-20 per second
+ */
+FUNCTION(grub_get_rtc)
+       pushl   %ebp
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       /* %ax is already zero */
+        int    $0x1a
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %ecx, %eax
+       shll    $16, %eax
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ * unsigned char grub_vga_set_mode (unsigned char mode)
+ */
+FUNCTION(grub_vga_set_mode)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %ebx
+       movl    %eax, %ecx
+
+       call    prot_to_real
+       .code16
+       /* get current mode */
+       xorw    %bx, %bx
+       movb    $0x0f, %ah
+       int     $0x10
+       movb    %al, %dl
+
+       /* set the new mode */
+       movb    %cl, %al
+       xorb    %ah, %ah
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %al
+       popl    %ebx
+       popl    %ebp
+       ret
+
+
+/*
+ * unsigned char *grub_vga_get_font (void)
+ */
+FUNCTION(grub_vga_get_font)
+       pushl   %ebp
+       pushl   %ebx
+
+       call    prot_to_real
+       .code16
+       movw    $0x1130, %ax
+       movb    $0x06, %bh
+       int     $0x10
+       movw    %es, %bx
+       movw    %bp, %dx
+       DATA32  call    real_to_prot
+       .code32
+
+       movzwl  %bx, %ecx
+       shll    $4, %ecx
+       movw    %dx, %ax
+       addl    %ecx, %eax
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_controller_info (struct
grub_vbe_info_block *controller_info)
+ *
+ * Register allocations for parameters:
+ * %eax                *controller_info
+ */
+FUNCTION(grub_vbe_get_controller_info)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %edi
+
+       movw    %ax, %di        /* Store *controller_info to %edx:%di.
*/
+       xorw    %ax, %ax
+       shrl    $4, %eax
+       mov     %eax, %edx      /* prot_to_real destroys %eax.  */
+       
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *controller_info is now on %es:%di.
*/
+       movw    $0x4f00, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
+       
+       popl    %edi
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_mode_info (grub_uint32_t mode,
+ *                                          struct
grub_vbe_mode_info_block *mode_info)
+ *
+ * Register allocations for parameters:
+ * %eax                mode
+ * %edx                *mode_info
+ */
+FUNCTION(grub_vbe_get_mode_info)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %edi
+
+       movl    %eax, %ecx      /* Store mode number to %ecx.  */
+
+       movw    %dx, %di        /* Store *mode_info to %edx:%di.  */
+       xorw    %dx, %dx
+       shrl    $4, %edx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *mode_info is now on %es:%di.  */
+       movw    $0x4f01, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
+
+       popl    %edi
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_mode (grub_uint32_t mode,
+ *                                     struct grub_vbe_crtc_info_block
*crtc_info)
+ *
+ * Register allocations for parameters:
+ * %eax                mode
+ * %edx                *crtc_info
+ */
+FUNCTION(grub_vbe_set_mode)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx      /* Store mode in %ebx.  */
+
+       movw    %dx, %di        /* Store *crtc_info to %edx:%di.  */
+       xorw    %dx, %dx
+       shrl    $4, %edx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *crtc_info is now on %es:%di.  */
+
+       movw    $0x4f02, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_mode (grub_uint32_t *mode)
+ *
+ * Register allocations for parameters:
+ * %eax                *mode
+ */
+FUNCTION(grub_vbe_get_mode)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %eax            /* Push *mode to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f03, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *mode from stack to %edi.  */
+       andl    $0xFFFF, %ebx
+       movl    %ebx, (%edi)
+
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_memory_window (grub_uint32_t window,
+ *                                              grub_uint32_t
position);
+ *
+ * Register allocations for parameters:
+ * %eax                window
+ * %edx                position
+ */
+FUNCTION(grub_vbe_set_memory_window)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f05, %ax
+       andw    $0x00ff, %bx    /* BL = window, BH = 0, Set memory
window.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_memory_window (grub_uint32_t window,
+ *                                              grub_uint32_t
*position);
+ *
+ * Register allocations for parameters:
+ * %eax                window
+ * %edx                *position
+ */
+FUNCTION(grub_vbe_get_memory_window)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edx            /* Push *position to stack.  */
+
+       movl    %eax, %ebx      /* Store window in %ebx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f05, %ax
+       andw    $0x00ff, %bx    /* BL = window.  */
+       orw     $0x0100, %bx    /* BH = 1, Get memory window.  */
+       int     $0x10
+
+       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* pops *position from stack to %edi.
*/
+       andl    $0xFFFF, %edx
+       movl    %edx, (%edi)    /* Return position to caller.  */
+
+       movw    %bx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_scanline_length (grub_uint32_t
length)
+ *
+ * Register allocations for parameters:
+ * %eax                length
+ */
+FUNCTION(grub_vbe_set_scanline_length)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ecx      /* Store length in %ecx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f06, %ax
+       movw    $0x0002, %bx    /* BL = 2, Set Scan Line in Bytes.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_scanline_length (grub_uint32_t
*length)
+ *
+ * Register allocations for parameters:
+ * %eax                *length
+ */
+FUNCTION(grub_vbe_get_scanline_length)
+       mov     0x4(%esp), %eax
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edx            /* Push *length to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f06, %ax
+       movw    $0x0001, %bx    /* BL = 1, Get Scan Line Length (in
bytes).  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *length from stack to %edi.  */
+       andl    $0xFFFF, %ebx
+       movl    %ebx, (%edi)    /* Return length to caller.  */
+
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_display_start (grub_uint32_t x,
+ *                                              grub_uint32_t y)
+ *
+ * Register allocations for parameters:
+ * %eax                x
+ * %edx                y
+ */
+FUNCTION(grub_vbe_set_display_start)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ecx      /* Store x in %ecx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f07, %ax
+       movw    $0x0080, %bx    /* BL = 80h, Set Display Start 
+                                  during Vertical Retrace.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_display_start (grub_uint32_t *x,
+ *                                              grub_uint32_t *y)
+ *
+ * Register allocations for parameters:
+ * %eax                *x
+ * %edx                *y
+ */
+FUNCTION(grub_vbe_get_display_start)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %eax            /* Push *x to stack.  */
+       pushl   %edx            /* Push *y to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f07, %ax
+       movw    $0x0001, %bx    /* BL = 1, Get Display Start.  */
+       int     $0x10
+
+       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *y from stack to %edi.  */
+       andl    $0xFFFF, %edx
+       movl    %edx, (%edi)    /* Return y-position to caller.  */
+
+       popl    %edi            /* Pops *x from stack to %edi.  */
+       andl    $0xFFFF, %ecx
+       movl    %ecx, (%edi)    /* Return x-position to caller.  */
+
+       movw    %bx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_palette_data (grub_uint32_t
color_count,
+ *                                             grub_uint32_t
start_index,
+ *                                             struct
grub_vbe_palette_data *palette_data)
+ *
+ * Register allocations for parameters:
+ * %eax                color_count
+ * %edx                start_index
+ * %ecx                *palette_data
+ */
+FUNCTION(grub_vbe_set_palette_data)
+       mov     0x4(%esp), %eax
+       mov     0x8(%esp), %edx
+       mov     0xC(%esp), %ecx
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx      /* Store color_count in %ebx.  */
+
+       movw    %cx, %di        /* Store *palette_data to %ecx:%di.  */
+       xorw    %cx, %cx
+       shrl    $4, %ecx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %cx, %es        /* *palette_data is now on %es:%di.  */
+       movw    %bx, %cx        /* color_count is now on %cx.  */
+
+       movw    $0x4f09, %ax
+       xorw    %bx, %bx        /* BL = 0, Set Palette Data.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret

FILE kern/i386/pc/startup.S.regparm (NEW)

        (1) The old startup.S file. Only renamed.

diff -EbwBruN ./kern/i386/pc/startup.S.regparm
../../src/grub-1.93/kern/i386/pc/startup.S.regparm
--- ./kern/i386/pc/startup.S.regparm    1970-01-01 01:00:00.000000000
+0100
+++ ../../src/grub-1.93/kern/i386/pc/startup.S.regparm  2006-05-03
22:39:20.000000000 +0200
@@ -0,0 +1,2170 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2005 Free Software
Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or
modify
+ *  it under the terms of the GNU General Public License as published
by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ * Note: These functions defined in this file may be called from C.
+ *       Be careful of that you must not modify some registers. Quote
+ *       from gcc-2.95.2/gcc/config/i386/i386.h:
+       
+   1 for registers not available across function calls.
+   These must include the FIXED_REGISTERS and also any
+   registers that can be used without being saved.
+   The latter must include the registers where values are returned
+   and the register where structure-value addresses are passed.
+   Aside from that, you can include as many other registers as you
like.
+
+  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
+{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
+ */
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ *       So the first three arguments are passed in %eax, %edx, and
%ecx,
+ *       respectively, and if a function has a fixed number of
arguments
+ *       and the number if greater than three, the function must return
+ *       with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+#include <grub/machine/boot.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/console.h>
+#include <grub/machine/linux.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/multiboot.h>
+               
+#define ABS(x) ((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR +
0x200)
+       
+       .file   "startup.S"
+
+       .text
+
+       /* Tell GAS to generate 16-bit instructions so that this code
works
+          in real mode. */
+       .code16
+
+       .globl  start, _start
+start:
+_start:
+       /*
+        *  Guarantee that "main" is loaded at 0x0:0x8200.
+        */
+       ljmp $0, $ABS(codestart)
+
+       /*
+        *  Compatibility version number
+        *
+        *  These MUST be at byte offset 6 and 7 of the executable
+        *  DO NOT MOVE !!!
+        */
+       . = EXT_C(start) + 0x6
+       .byte   GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+
+       /*
+        *  This is a special data area 8 bytes from the beginning.
+        */
+
+       . = EXT_C(start) + 0x8
+
+VARIABLE(grub_total_module_size)
+       .long   0
+VARIABLE(grub_kernel_image_size)
+       .long   0
+VARIABLE(grub_compressed_size)
+       .long   0
+VARIABLE(grub_install_dos_part)
+       .long   0xFFFFFFFF
+VARIABLE(grub_install_bsd_part)
+       .long   0xFFFFFFFF
+VARIABLE(grub_prefix)
+       .string "/boot/grub"
+
+       /*
+        *  Leave some breathing room for the prefix.
+        */
+
+       . = EXT_C(start) + 0x50
+
+/*
+ * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB
itself).
+ * This uses the a.out kludge to load raw binary to the area starting
at 1MB,
+ * and relocates itself after loaded.
+ */
+multiboot_header:
+       /* magic */
+       .long   0x1BADB002
+       /* flags */
+       .long   (1 << 16)
+       /* checksum */
+       .long   -0x1BADB002 - (1 << 16)
+       /* header addr */
+       .long   multiboot_header - _start + 0x100000 + 0x200
+       /* load addr */
+       .long   0x100000
+       /* load end addr */
+       .long   0
+       /* bss end addr */
+       .long   0
+       /* entry addr */
+       .long   multiboot_entry - _start + 0x100000 + 0x200
+       
+multiboot_entry:
+       .code32
+       /* obtain the boot device */
+       movl    12(%ebx), %edx
+
+       /* relocate the code */
+       movl    $(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx
+       addl    EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200,
%ecx
+       movl    $0x100000, %esi
+       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi
+       cld
+       rep
+       movsb
+       /* jump to the real address */
+       movl    $multiboot_trampoline, %eax
+       jmp     *%eax
+       
+multiboot_trampoline:
+       /* fill the boot information */
+       movl    %edx, %eax
+       shrl    $8, %eax
+       xorl    %ebx, %ebx
+       cmpb    $0xFF, %ah
+       je      1f
+       movb    %ah, %bl
+       movl    %ebx, EXT_C(grub_install_dos_part)
+1:
+       cmpb    $0xFF, %al
+       je      2f
+       movb    %al, %bl
+       movl    %ebx, EXT_C(grub_install_bsd_part)
+2:
+       shrl    $24, %edx
+       /* enter the usual booting */
+       call    prot_to_real
+       .code16
+       
+/* the real mode code continues... */
+codestart:
+       cli             /* we're not safe here! */
+
+       /* set up %ds, %ss, and %es */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       movw    %ax, %ss
+       movw    %ax, %es
+
+       /* set up the real mode/BIOS stack */
+       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
+       movl    %ebp, %esp
+
+       sti             /* we're safe again */
+
+       /* save boot drive reference */
+       ADDR32  movb    %dl, EXT_C(grub_boot_drive)
+
+       /* reset disk system (%ah = 0) */
+       int     $0x13
+       
+       /* transition to protected mode */
+       DATA32  call real_to_prot
+
+       /* The ".code32" directive takes GAS out of 16-bit mode. */
+       .code32
+
+       incl    %eax
+       call    EXT_C(grub_gate_a20)
+       
+       /* decompress the compressed part and put the result at 1MB */
+       movl    $0x100000, %esi
+       movl    $(START_SYMBOL + GRUB_KERNEL_MACHINE_RAW_SIZE), %edi
+
+       pushl   %esi
+       pushl   EXT_C(grub_compressed_size)
+       pushl   %edi
+       call    lzo1x_decompress
+       addl    $12, %esp
+
+       /* copy back the decompressed part */
+       movl    %eax, %ecx
+       cld
+       rep
+       movsb
+
+       /* copy modules before cleaning out the bss */
+       movl    EXT_C(grub_total_module_size), %ecx
+       movl    EXT_C(grub_kernel_image_size), %esi
+       addl    %ecx, %esi
+       addl    $START_SYMBOL, %esi
+       decl    %esi
+       movl    $END_SYMBOL, %edi
+       addl    %ecx, %edi
+       decl    %edi
+       std
+       rep
+       movsb
+       
+       /* clean out the bss */
+       movl    $BSS_START_SYMBOL, %edi
+
+       /* compute the bss length */
+       movl    $END_SYMBOL, %ecx
+       subl    %edi, %ecx
+                       
+       /* clean out */
+       xorl    %eax, %eax
+       cld
+       rep
+       stosb
+       
+       /*
+        *  Call the start of main body of C code.
+        */
+       call EXT_C(grub_main)
+
+
+/*
+ *  This is the area for all of the special variables.
+ */
+
+       .p2align        2       /* force 4-byte alignment */
+
+protstack:
+       .long   GRUB_MEMORY_MACHINE_PROT_STACK
+
+VARIABLE(grub_boot_drive)
+       .long   0
+
+VARIABLE(grub_start_addr)
+       .long   START_SYMBOL
+
+VARIABLE(grub_end_addr)
+       .long   END_SYMBOL
+       
+VARIABLE(grub_apm_bios_info)
+       .word   0       /* version */
+       .word   0       /* cseg */
+       .long   0       /* offset */
+       .word   0       /* cseg_16 */
+       .word   0       /* dseg_16 */
+       .word   0       /* cseg_len */
+       .word   0       /* cseg_16_len */
+       .word   0       /* dseg_16_len */
+       
+/*
+ * This is the Global Descriptor Table
+ *
+ *  An entry, a "Segment Descriptor", looks like this:
+ *
+ * 31          24         19   16                 7           0
+ * ------------------------------------------------------------
+ * |             | |B| |A|       | |   |1|0|E|W|A|            |
+ * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |  4
+ * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
+ * ------------------------------------------------------------
+ * |                             |                            |
+ * |        BASE 15..0           |       LIMIT 15..0          |  0
+ * |                             |                            |
+ * ------------------------------------------------------------
+ *
+ *  Note the ordering of the data items is reversed from the above
+ *  description.
+ */
+
+       .p2align        2       /* force 4-byte alignment */
+gdt:
+       .word   0, 0
+       .byte   0, 0, 0, 0
+
+       /* -- code segment --
+        * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity),
present
+        * type = 32bit code execute/read, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x9A, 0xCF, 0
+
+       /* -- data segment --
+        * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+        * type = 32 bit data read/write, DPL = 0 
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x92, 0xCF, 0
+
+       /* -- 16 bit real mode CS --
+        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+        * type = 16 bit code execute/read only/conforming, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x9E, 0, 0
+
+       /* -- 16 bit real mode DS --
+        * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+        * type = 16 bit data read/write, DPL = 0
+        */
+       .word   0xFFFF, 0
+       .byte   0, 0x92, 0, 0
+
+
+/* this is the GDT descriptor */
+gdtdesc:
+       .word   0x27                    /* limit */
+       .long   gdt                     /* addr */
+
+       
+/*
+ *  These next two routines, "real_to_prot" and "prot_to_real" are
structured
+ *  in a very specific way.  Be very careful when changing them.
+ *
+ *  NOTE:  Use of either one messes up %eax and %ebp.
+ */
+
+real_to_prot:
+       .code16
+       cli
+
+       /* load the GDT register */
+       DATA32  ADDR32  lgdt    gdtdesc
+
+       /* turn on protected mode */
+       movl    %cr0, %eax
+       orl     $GRUB_MEMORY_MACHINE_CR0_PE_ON, %eax
+       movl    %eax, %cr0
+
+       /* jump to relocation, flush prefetch queue, and reload %cs */
+       DATA32  ljmp    $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
+
+       .code32
+protcseg:
+       /* reload other segment registers */
+       movw    $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* put the return address in a known safe location */
+       movl    (%esp), %eax
+       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+       /* get protected mode stack */
+       movl    protstack, %eax
+       movl    %eax, %esp
+       movl    %eax, %ebp
+
+       /* get return address onto the right stack */
+       movl    GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+       movl    %eax, (%esp)
+
+       /* zero %eax */
+       xorl    %eax, %eax
+
+       /* return on the old (or initialized) stack! */
+       ret
+
+
+prot_to_real:
+       /* just in case, set GDT */
+       lgdt    gdtdesc
+
+       /* save the protected mode stack */
+       movl    %esp, %eax
+       movl    %eax, protstack
+
+       /* get the return address */
+       movl    (%esp), %eax
+       movl    %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+       /* set up new stack */
+       movl    $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+       movl    %eax, %esp
+       movl    %eax, %ebp
+
+       /* set up segment limits */
+       movw    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* this might be an extra step */
+       /* jump to a 16 bit segment */
+       ljmp    $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
+
+tmpcseg:
+       .code16
+
+       /* clear the PE bit of CR0 */
+       movl    %cr0, %eax
+       andl    $(~GRUB_MEMORY_MACHINE_CR0_PE_ON), %eax
+       movl    %eax, %cr0
+
+       /* flush prefetch queue, reload %cs */
+       DATA32  ljmp    $0, $realcseg
+
+realcseg:
+       /* we are in real mode now
+        * set up the real mode segment registers : DS, SS, ES
+        */
+       /* zero %eax */
+       xorl    %eax, %eax
+
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+       movw    %ax, %ss
+
+       /* restore interrupts */
+       sti
+
+       /* return on new stack! */
+       DATA32  ret
+
+       .code32
+
+
+/*
+ * grub_gate_a20(int on)
+ *
+ * Gate address-line 20 for high memory.
+ *
+ * This routine is probably overconservative in what it does, but so
what?
+ *
+ * It also eats any keystrokes in the keyboard buffer.  :-(
+ */
+
+FUNCTION(grub_gate_a20)
+       movl    %eax, %edx
+
+gate_a20_test_current_state:   
+       /* first of all, test if already in a good state */
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_bios
+       ret
+
+gate_a20_try_bios:
+       /* second, try a BIOS call */
+       pushl   %ebp
+       call    prot_to_real
+
+       .code16
+       movw    $0x2400, %ax
+       testb   %dl, %dl
+       jz      1f
+       incw    %ax
+1:     int     $0x15
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebp
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_keyboard_controller
+       ret
+       
+gate_a20_flush_keyboard_buffer:
+       inb     $0x64
+       andb    $0x02, %al
+       jnz     gate_a20_flush_keyboard_buffer
+2:     
+       inb     $0x64
+       andb    $0x01, %al
+       jz      3f
+       inb     $0x60
+       jmp     2b
+3:
+       ret
+       
+gate_a20_try_keyboard_controller:      
+       /* third, try the keyboard controller */
+       call    gate_a20_flush_keyboard_buffer
+
+       movb    $0xd1, %al
+       outb    $0x64
+4:     
+       inb     $0x64
+       andb    $0x02, %al
+       jnz     4b
+
+       movb    $0xdd, %al
+       testb   %dl, %dl
+       jz      5f
+       orb     $0x02, %al
+5:     outb    $0x60
+       call    gate_a20_flush_keyboard_buffer
+
+       /* output a dummy command (USB keyboard hack) */
+       movb    $0xff, %al
+       outb    $0x64
+       call    gate_a20_flush_keyboard_buffer
+
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       jnz     gate_a20_try_system_control_port_a
+       ret
+
+gate_a20_try_system_control_port_a:
+       /* fourth, try the system control port A */
+       inb     $0x92
+       andb    $(~0x03), %al
+       testb   %dl, %dl
+       jz      6f
+       orb     $0x02, %al
+6:     outb    $0x92
+
+       /* When turning off Gate A20, do not check the state strictly,
+          because a failure is not fatal usually, and Gate A20 is
always
+          on some modern machines.  */
+       testb   %dl, %dl
+       jz      7f      
+       call    gate_a20_check_state
+       cmpb    %al, %dl
+       /* everything failed, so restart from the beginning */
+       jnz     gate_a20_try_bios
+7:     ret
+       
+gate_a20_check_state:
+       /* iterate the checking for a while */
+       movl    $100, %ecx
+1:     
+       call    3f
+       cmpb    %al, %dl
+       jz      2f
+       loop    1b
+2:
+       ret
+3:     
+       pushl   %ebx
+       pushl   %ecx
+       xorl    %eax, %eax
+       /* compare the byte at 0x8000 with that at 0x108000 */
+       movl    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx
+       pushl   %ebx
+       /* save the original byte in CL */
+       movb    (%ebx), %cl
+       /* store the value at 0x108000 in AL */
+       addl    $0x100000, %ebx
+       movb    (%ebx), %al
+       /* try to set one less value at 0x8000 */
+       popl    %ebx
+       movb    %al, %ch
+       decb    %ch
+       movb    %ch, (%ebx)
+       /* serialize */
+       outb    %al, $0x80
+       outb    %al, $0x80
+       /* obtain the value at 0x108000 in CH */
+       pushl   %ebx
+       addl    $0x100000, %ebx
+       movb    (%ebx), %ch
+       /* this result is 1 if A20 is on or 0 if it is off */
+       subb    %ch, %al
+       xorb    $1, %al
+       /* restore the original */
+       popl    %ebx
+       movb    %cl, (%ebx)
+       popl    %ecx
+       popl    %ebx
+       ret
+
+#include "lzo1x.S"
+
+
+/*
+ *  This call is special...  it never returns...  in fact it should
simply
+ *  hang at this point!
+ */
+
+FUNCTION(grub_stop)
+       call    prot_to_real
+
+       /*
+        * This next part is sort of evil.  It takes advantage of the
+        * byte ordering on the x86 to work in either 16-bit or 32-bit
+        * mode, so think about it before changing it.
+        */
+
+FUNCTION(grub_hard_stop)
+       hlt
+       jmp EXT_C(grub_hard_stop)
+
+
+/*
+ * grub_stop_floppy()
+ *
+ * Stop the floppy drive from spinning, so that other software is
+ * jumped to with a known state.
+ */
+FUNCTION(grub_stop_floppy)
+       movw    $0x3F2, %dx
+       xorb    %al, %al
+       outb    %al, %dx
+       ret
+
+/*
+ * grub_reboot()
+ *
+ * Reboot the system. At the moment, rely on BIOS.
+ */
+FUNCTION(grub_reboot)
+       call    prot_to_real
+       .code16
+       /* cold boot */
+       movw    $0x0472, %di
+       movw    %ax, (%di)
+       ljmp    $0xFFFF, $0x0000
+       .code32
+       
+/*
+ * grub_halt(int no_apm)
+ *
+ * Halt the system, using APM if possible. If NO_APM is true, don't use
+ * APM even if it is available.
+ */
+FUNCTION(grub_halt)
+       /* see if zero */
+       testl   %eax, %eax
+       jnz     EXT_C(grub_stop)
+
+       call    prot_to_real
+       .code16
+       
+       /* detect APM */
+       movw    $0x5300, %ax
+       xorw    %bx, %bx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+       /* don't check %bx for buggy BIOSes... */
+
+       /* disconnect APM first */
+       movw    $0x5304, %ax
+       xorw    %bx, %bx
+       int     $0x15
+
+       /* connect APM */
+       movw    $0x5301, %ax
+       xorw    %bx, %bx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+
+       /* set APM protocol level - 1.1 or bust. (this covers APM 1.2
also) */
+       movw    $0x530E, %ax
+       xorw    %bx, %bx
+       movw    $0x0101, %cx
+       int     $0x15
+       jc      EXT_C(grub_hard_stop)
+       
+       /* set the power state to off */
+       movw    $0x5307, %ax
+       movw    $1, %bx
+       movw    $3, %cx
+       int     $0x15
+
+       /* shouldn't reach here */
+       jmp     EXT_C(grub_hard_stop)
+       .code32
+       
+       
+/*
+ *  void grub_chainloader_real_boot (int drive, void *part_addr)
+ *
+ *  This starts another boot loader.
+ */
+
+FUNCTION(grub_chainloader_real_boot)
+       pushl   %edx
+       pushl   %eax
+
+       call    EXT_C(grub_dl_unload_all)
+
+       /* set up to pass boot drive */
+       popl    %edx
+
+       /* ESI must point to a partition table entry */
+       popl    %esi
+
+       /* Turn off Gate A20 */
+       xorl    %eax, %eax
+       call    EXT_C(grub_gate_a20)
+       
+       call    prot_to_real
+       .code16
+       ljmp    $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
+       .code32
+
+
+/*
+ * void grub_linux_boot_zimage (void)
+ */
+VARIABLE(grub_linux_prot_size)
+       .long   0
+VARIABLE(grub_linux_tmp_addr)
+       .long   0
+VARIABLE(grub_linux_real_addr)
+       .long   0
+       
+FUNCTION(grub_linux_boot_zimage)
+       /* copy the kernel */
+       movl    EXT_C(grub_linux_prot_size), %ecx
+       addl    $3, %ecx
+       shrl    $2, %ecx
+       movl    $GRUB_LINUX_BZIMAGE_ADDR, %esi
+       movl    $GRUB_LINUX_ZIMAGE_ADDR, %edi
+       cld
+       rep
+       movsl
+
+FUNCTION(grub_linux_boot_bzimage)
+       call    EXT_C(grub_dl_unload_all)
+       
+       movl    EXT_C(grub_linux_real_addr), %ebx
+
+       /* copy the real mode code */
+       movl    EXT_C(grub_linux_tmp_addr), %esi
+       movl    %ebx, %edi
+       movl    $GRUB_LINUX_SETUP_MOVE_SIZE, %ecx
+       cld
+       rep
+       movsb
+
+       /* change %ebx to the segment address */
+       shrl    $4, %ebx
+       movl    %ebx, %eax
+       addl    $0x20, %eax
+       movw    %ax, linux_setup_seg
+
+       /* XXX new stack pointer in safe area for calling functions */
+       movl    $0x4000, %esp
+       call    EXT_C(grub_stop_floppy)
+
+       /* final setup for linux boot */
+       call    prot_to_real
+       .code16
+
+       cli
+       movw    %bx, %ss
+       movw    $GRUB_LINUX_SETUP_STACK, %sp
+
+       movw    %bx, %ds
+       movw    %bx, %es
+       movw    %bx, %fs
+       movw    %bx, %gs
+
+       /* ljmp */
+       .byte   0xea
+       .word   0
+linux_setup_seg:
+       .word   0
+       .code32
+
+               
+/*
+ * This starts the multiboot kernel.
+ */
+
+FUNCTION(grub_multiboot_real_boot)
+       /* Push the entry address on the stack.  */
+       pushl   %eax
+       /* Move the address of the multiboot information structure to
ebx.  */
+       movl    %edx,%ebx
+       
+       /* Unload all modules and stop the floppy driver.  */
+       call    EXT_C(grub_dl_unload_all)
+       call    EXT_C(grub_stop_floppy)
+
+       /* Interrupts should be disabled.  */
+       cli
+       
+       /* Move the magic value into eax and jump to the kernel.  */
+       movl    $GRUB_MB_MAGIC2,%eax
+       popl    %ecx
+       jmp     *%ecx
+       
+       
+/*
+ *   int grub_biosdisk_rw_int13_extensions (int ah, int drive, void
*dap)
+ *
+ *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
+ *   is passed for disk address packet. If an error occurs, return
+ *   non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_rw_int13_extensions)
+       pushl   %ebp
+       pushl   %esi
+
+       /* compute the address of disk_address_packet */
+       movw    %cx, %si
+       xorw    %cx, %cx
+       shrl    $4, %ecx        /* save the segment to cx */
+
+       /* ah */
+       movb    %al, %dh
+       /* enter real mode */
+       call    prot_to_real
+       
+       .code16
+       movb    %dh, %ah
+       movw    %cx, %ds
+       int     $0x13           /* do the operation */
+       movb    %ah, %dl        /* save return value */
+       /* clear the data segment */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %al        /* return value in %eax */
+
+       popl    %esi
+       popl    %ebp
+
+       ret
+       
+/*
+ *   int grub_biosdisk_rw_standard (int ah, int drive, int coff, int
hoff,
+ *                                  int soff, int nsec, int segment)
+ *
+ *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
+ *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
+ *   return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_rw_standard)
+       pushl   %ebp
+       movl    %esp, %ebp
+
+       pushl   %ebx
+       pushl   %edi
+       pushl   %esi
+
+       /* set up CHS information */
+
+       /* set %ch to low eight bits of cylinder */
+       xchgb   %cl, %ch
+       /* set bits 6-7 of %cl to high two bits of cylinder */
+       shlb    $6, %cl
+       /* set bits 0-5 of %cl to sector */
+       addb    0xc(%ebp), %cl
+       /* set %dh to head */
+       movb    0x8(%ebp), %dh
+       /* set %ah to AH */
+       movb    %al, %ah
+       /* set %al to NSEC */
+       movb    0x10(%ebp), %al
+       /* save %ax in %di */
+       movw    %ax, %di
+       /* save SEGMENT in %bx */
+       movw    0x14(%ebp), %bx
+               
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movw    %bx, %es
+       xorw    %bx, %bx
+       movw    $3, %si         /* attempt at least three times */
+
+1:     
+       movw    %di, %ax
+       int     $0x13           /* do the operation */
+       jnc     2f              /* check if successful */
+
+       movb    %ah, %bl        /* save return value */
+       /* if fail, reset the disk system */
+       xorw    %ax, %ax
+       int     $0x13
+       
+       decw    %si
+       cmpw    $0, %si
+       je      2f
+       xorb    %bl, %bl
+       jmp     1b              /* retry */
+2:     
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+       
+       popl    %esi
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+
+       ret     $(4 * 4)
+
+
+/*
+ *   int grub_biosdisk_check_int13_extensions (int drive)
+ *
+ *   Check if LBA is supported for DRIVE. If it is supported, then
return
+ *   the major version of extensions, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_check_int13_extensions)
+       pushl   %ebp
+       pushl   %ebx
+
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x41, %ah
+       movw    $0x55aa, %bx
+       int     $0x13           /* do the operation */
+       
+       /* check the result */
+       jc      1f
+       cmpw    $0xaa55, %bx
+       jne     1f
+
+       movb    %ah, %bl        /* save the major version into %bl */
+
+       /* check if AH=0x42 is supported */
+       andw    $1, %cx
+       jnz     2f
+       
+1:
+       xorb    %bl, %bl
+2:
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %ebx
+       popl    %ebp
+
+       ret
+
+
+/*
+ *   int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void
*drp)
+ *
+ *   Return the geometry of DRIVE in a drive parameters, DRP. If an
error
+ *   occurs, then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %esi
+
+       /* compute the address of drive parameters */
+       movw    %dx, %si
+       xorw    %dx, %dx
+       shrl    $4, %edx
+       movw    %dx, %bx        /* save the segment into %bx */
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x48, %ah
+       movw    %bx, %ds
+       int     $0x13           /* do the operation */
+       movb    %ah, %bl        /* save return value in %bl */
+       /* clear the data segment */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %esi
+       popl    %ebx
+       popl    %ebp
+       
+       ret
+
+
+/*
+ *   int grub_biosdisk_get_diskinfo_standard (int drive,
+ *                                            unsigned long *cylinders,

+ *                                            unsigned long *heads,
+ *                                            unsigned long *sectors)
+ *
+ *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If
an
+ *   error occurs, then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_diskinfo_standard)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+
+       /* push CYLINDERS */
+       pushl   %edx
+       /* push HEADS */
+       pushl   %ecx
+       /* SECTORS is on the stack */
+       
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+
+       .code16
+       movb    $0x8, %ah
+       int     $0x13           /* do the operation */
+       /* check if successful */
+       testb   %ah, %ah
+       jnz     1f
+       /* bogus BIOSes may not return an error number */
+       testb   $0x3f, %cl      /* 0 sectors means no disk */
+       jnz     1f              /* if non-zero, then succeed */
+       /* XXX 0x60 is one of the unused error numbers */
+       movb    $0x60, %ah
+1:
+       movb    %ah, %bl        /* save return value in %bl */
+       /* back to protected mode */
+       DATA32  call    real_to_prot
+       .code32
+
+       /* pop HEADS */
+       popl    %edi
+       movb    %dh, %al
+       incl    %eax    /* the number of heads is counted from zero */
+       movl    %eax, (%edi)
+
+       /* pop CYLINDERS */
+       popl    %edi
+       movb    %ch, %al
+       movb    %cl, %ah
+       shrb    $6, %ah /* the number of cylinders is counted from zero
*/
+       incl    %eax
+       movl    %eax, (%edi)
+
+       /* SECTORS */
+       movl    0x10(%esp), %edi
+       andb    $0x3f, %cl
+       movzbl  %cl, %eax
+       movl    %eax, (%edi)
+       
+       xorl    %eax, %eax
+       movb    %bl, %al        /* return value in %eax */
+
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+
+       ret     $4
+
+
+/*
+ * int grub_biosdisk_get_num_floppies (void)
+ */
+FUNCTION(grub_biosdisk_get_num_floppies)
+       pushl   %ebp
+
+       xorl    %edx, %edx
+       call    prot_to_real
+       
+       .code16
+       /* reset the disk system first */
+       int     $0x13
+1:
+       stc
+       
+       /* call GET DISK TYPE */
+       movb    $0x15, %ah
+       int     $0x13
+
+       jc      2f
+
+       /* check if this drive exists */        
+       testb   $0x3, %ah
+       jz      2f
+
+       incb    %dl
+       cmpb    $2, %dl
+       jne     1b
+2:
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       popl    %ebp
+       ret
+       
+       
+/*
+ *
+ * grub_get_memsize(i) :  return the memory size in KB. i == 0 for
conventional
+ *             memory, i == 1 for extended memory
+ *     BIOS call "INT 12H" to get conventional memory size
+ *     BIOS call "INT 15H, AH=88H" to get extended memory size
+ *             Both have the return value in AX.
+ *
+ */
+
+FUNCTION(grub_get_memsize)
+       pushl   %ebp
+
+       movl    %eax, %edx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       testl   %edx, %edx
+       jnz     xext
+
+       int     $0x12
+       jmp     xdone
+
+xext:
+       movb    $0x88, %ah
+       int     $0x15
+
+xdone:
+       movw    %ax, %dx
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ *
+ * grub_get_eisa_mmap() :  return packed EISA memory map, lower 16 bits
is
+ *             memory between 1M and 16M in 1K parts, upper 16 bits is
+ *             memory above 16M in 64K parts.  If error, return zero.
+ *     BIOS call "INT 15H, AH=E801H" to get EISA memory map,
+ *             AX = memory between 1M and 16M in 1K parts.
+ *             BX = memory above 16M in 64K parts.
+ *
+ */
+
+FUNCTION(grub_get_eisa_mmap)
+       pushl   %ebp
+       pushl   %ebx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movw    $0xe801, %ax
+       int     $0x15
+
+       shll    $16, %ebx
+       movw    %ax, %bx
+
+       DATA32  call    real_to_prot
+       .code32
+
+       cmpb    $0x86, %bh
+       je      xnoteisa
+
+       movl    %ebx, %eax
+
+xnoteisa:
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ *
+ * grub_get_mmap_entry(addr, cont) : address and old continuation value
(zero to
+ *             start), for the Query System Address Map BIOS call.
+ *
+ *  Sets the first 4-byte int value of "addr" to the size returned by
+ *  the call.  If the call fails, sets it to zero.
+ *
+ *     Returns:  new (non-zero) continuation value, 0 if done.
+ */
+
+FUNCTION(grub_get_mmap_entry)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       pushl   %esi
+
+       /* push ADDR */
+       pushl   %eax
+       
+       /* place address (+4) in ES:DI */
+       addl    $4, %eax
+       movl    %eax, %edi
+       andl    $0xf, %edi
+       shrl    $4, %eax
+       movl    %eax, %esi
+
+       /* set continuation value */
+       movl    %edx, %ebx
+
+       /* set default maximum buffer size */
+       movl    $0x14, %ecx
+
+       /* set EDX to 'SMAP' */
+       movl    $0x534d4150, %edx
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movw    %si, %es
+       movl    $0xe820, %eax
+       int     $0x15
+
+       DATA32  jc      xnosmap
+
+       cmpl    $0x534d4150, %eax
+       jne     xnosmap
+
+       cmpl    $0x14, %ecx
+       jl      xnosmap
+
+       cmpl    $0x400, %ecx
+       jg      xnosmap
+
+       jmp     xsmap
+
+xnosmap:
+       xorl    %ecx, %ecx
+
+xsmap:
+       DATA32  call    real_to_prot
+       .code32
+
+       /* write length of buffer (zero if error) into ADDR */
+       popl    %eax
+       movl    %ecx, (%eax)
+
+       /* set return value to continuation */
+       movl    %ebx, %eax
+
+       popl    %esi
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_real_putchar (int c)
+ *
+ * Put the character C on the console. Because GRUB wants to write a
+ * character with an attribute, this implementation is a bit tricky.
+ * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
+ * (TELETYPE OUTPUT). Otherwise, save the original position, put a
space,
+ * save the current position, restore the original position, write the
+ * character and the attribute, and restore the current position.
+ *
+ * The reason why this is so complicated is that there is no easy way
to
+ * get the height of the screen, and the TELETYPE OUPUT BIOS call
doesn't
+ * support setting a background attribute.
+ */
+FUNCTION(grub_console_real_putchar)
+       movl    %eax, %edx
+       pusha
+       movb    EXT_C(grub_console_cur_color), %bl
+       
+       call    prot_to_real
+       .code16
+       movb    %dl, %al
+       xorb    %bh, %bh
+
+       /* use teletype output if control character */
+       cmpb    $0x7, %al
+       je      1f
+       cmpb    $0x8, %al
+       je      1f
+       cmpb    $0xa, %al
+       je      1f
+       cmpb    $0xd, %al
+       je      1f
+
+       /* save the character and the attribute on the stack */
+       pushw   %ax
+       pushw   %bx
+       
+       /* get the current position */
+       movb    $0x3, %ah
+       int     $0x10
+
+       /* check the column with the width */
+       cmpb    $79, %dl
+       jl      2f
+       
+       /* print CR and LF, if next write will exceed the width */      
+       movw    $0x0e0d, %ax
+       int     $0x10
+       movb    $0x0a, %al
+       int     $0x10
+       
+       /* get the current position */
+       movb    $0x3, %ah
+       int     $0x10
+
+2:     
+       /* restore the character and the attribute */
+       popw    %bx
+       popw    %ax
+       
+       /* write the character with the attribute */
+       movb    $0x9, %ah
+       movw    $1, %cx
+       int     $0x10
+
+       /* move the cursor forward */
+       incb    %dl
+       movb    $0x2, %ah
+       int     $0x10
+
+       jmp     3f
+
+1:     movw    $1, %bx
+       movb    $0xe, %ah
+       int     $0x10
+       
+3:     DATA32  call    real_to_prot
+       .code32
+       
+       popa
+       ret
+       
+
+/*
+ * int grub_console_getkey (void)
+ * BIOS call "INT 16H Function 00H" to read character from keyboard
+ *     Call with       %ah = 0x0
+ *     Return:         %ah = keyboard scan code
+ *                     %al = ASCII character
+ */
+
+/* this table is used in translate_keycode below */
+translation_table:
+       .word   GRUB_CONSOLE_KEY_LEFT, 2
+       .word   GRUB_CONSOLE_KEY_RIGHT, 6
+       .word   GRUB_CONSOLE_KEY_UP, 16
+       .word   GRUB_CONSOLE_KEY_DOWN, 14
+       .word   GRUB_CONSOLE_KEY_HOME, 1
+       .word   GRUB_CONSOLE_KEY_END, 5
+       .word   GRUB_CONSOLE_KEY_DC, 4
+       .word   GRUB_CONSOLE_KEY_BACKSPACE, 8
+       .word   GRUB_CONSOLE_KEY_PPAGE, 7
+       .word   GRUB_CONSOLE_KEY_NPAGE, 3
+       .word   0
+       
+/*
+ * translate_keycode translates the key code %dx to an ascii code.
+ */
+       .code16
+
+translate_keycode:
+       pushw   %bx
+       pushw   %si
+       
+       movw    $ABS(translation_table), %si
+       
+1:     lodsw
+       /* check if this is the end */
+       testw   %ax, %ax
+       jz      2f
+       /* load the ascii code into %ax */
+       movw    %ax, %bx
+       lodsw
+       /* check if this matches the key code */
+       cmpw    %bx, %dx
+       jne     1b
+       /* translate %dx, if successful */
+       movw    %ax, %dx
+
+2:     popw    %si
+       popw    %bx
+       ret
+
+       .code32
+       
+FUNCTION(grub_console_getkey)
+       pushl   %ebp
+
+       call    prot_to_real
+       .code16
+
+       int     $0x16
+
+       movw    %ax, %dx                /* real_to_prot uses %eax */
+       call    translate_keycode
+               
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ * int grub_console_checkkey (void)
+ *     if there is a character pending, return it; otherwise return -1
+ * BIOS call "INT 16H Function 01H" to check whether a character is
pending
+ *     Call with       %ah = 0x1
+ *     Return:
+ *             If key waiting to be input:
+ *                     %ah = keyboard scan code
+ *                     %al = ASCII character
+ *                     Zero flag = clear
+ *             else
+ *                     Zero flag = set
+ */
+FUNCTION(grub_console_checkkey)
+       pushl   %ebp
+       xorl    %edx, %edx
+       
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       movb    $0x1, %ah
+       int     $0x16
+
+       jz      notpending
+       
+       movw    %ax, %dx
+       DATA32  jmp     pending
+
+notpending:
+       decl    %edx
+
+pending:
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %edx, %eax
+
+       popl    %ebp
+       ret
+
+       
+/*
+ * grub_uint16_t grub_console_getxy (void)
+ * BIOS call "INT 10H Function 03h" to get cursor position
+ *     Call with       %ah = 0x03
+ *                     %bh = page
+ *      Returns         %ch = starting scan line
+ *                      %cl = ending scan line
+ *                      %dh = row (0 is top)
+ *                      %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_getxy)
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       call    prot_to_real
+       .code16
+
+        xorb   %bh, %bh                /* set page to 0 */
+       movb    $0x3, %ah
+       int     $0x10                   /* get cursor position */
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %ah
+       movb    %dh, %al
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+
+/*
+ * void grub_console_gotoxy(grub_uint8_t x, grub_uint8_t y)
+ * BIOS call "INT 10H Function 02h" to set cursor position
+ *     Call with       %ah = 0x02
+ *                     %bh = page
+ *                      %dh = row (0 is top)
+ *                      %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_gotoxy)
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       movb    %dl, %dh        /* %dh = y */
+       movb    %al, %dl        /* %dl = x */
+
+       call    prot_to_real
+       .code16
+
+        xorb   %bh, %bh                /* set page to 0 */
+       movb    $0x2, %ah
+       int     $0x10                   /* set cursor position */
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_cls (void)
+ * BIOS call "INT 10H Function 09h" to write character and attribute
+ *     Call with       %ah = 0x09
+ *                      %al = (character)
+ *                      %bh = (page number)
+ *                      %bl = (attribute)
+ *                      %cx = (number of times)
+ */
+
+FUNCTION(grub_console_cls)
+       pushl   %ebp
+       pushl   %ebx                    /* save EBX */
+
+       call    prot_to_real
+       .code16
+
+       /* move the cursor to the beginning */
+       movb    $0x02, %ah
+       xorb    %bh, %bh
+       xorw    %dx, %dx
+       int     $0x10
+
+       /* write spaces to the entire screen */
+       movw    $0x0920, %ax
+       movw    $0x07, %bx
+       movw    $(80 * 25), %cx
+        int    $0x10
+
+       /* move back the cursor */
+       movb    $0x02, %ah
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+       
+/*
+ * void grub_console_setcursor (int on)
+ * BIOS call "INT 10H Function 01h" to set cursor type
+ *      Call with       %ah = 0x01
+ *                      %ch = cursor starting scanline
+ *                      %cl = cursor ending scanline
+ */
+
+console_cursor_state:
+       .byte   1
+console_cursor_shape:
+       .word   0
+       
+FUNCTION(grub_console_setcursor)
+       pushl   %ebp
+       pushl   %ebx
+
+       /* push ON */
+       pushl   %eax
+       
+       /* check if the standard cursor shape has already been saved */
+       movw    console_cursor_shape, %ax
+       testw   %ax, %ax
+       jne     1f
+
+       call    prot_to_real
+       .code16
+
+       movb    $0x03, %ah
+       xorb    %bh, %bh
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movw    %cx, console_cursor_shape
+1:
+       /* set %cx to the designated cursor shape */
+       movw    $0x2000, %cx
+       popl    %eax
+       testl   %eax, %eax
+       jz      2f
+       movw    console_cursor_shape, %cx
+2:     
+       call    prot_to_real
+       .code16
+
+       movb    $0x1, %ah
+       int     $0x10 
+
+       DATA32  call    real_to_prot
+       .code32
+
+       popl    %ebx
+       popl    %ebp
+       ret
+               
+/*
+ * grub_getrtsecs()
+ *     if a seconds value can be read, read it and return it (BCD),
+ *      otherwise return 0xFF
+ * BIOS call "INT 1AH Function 02H" to check whether a character is
pending
+ *     Call with       %ah = 0x2
+ *     Return:
+ *             If RT Clock can give correct values
+ *                     %ch = hour (BCD)
+ *                     %cl = minutes (BCD)
+ *                      %dh = seconds (BCD)
+ *                      %dl = daylight savings time (00h std, 01h
daylight)
+ *                     Carry flag = clear
+ *             else
+ *                     Carry flag = set
+ *                         (this indicates that the clock is updating,
or
+ *                          that it isn't running)
+ */
+FUNCTION(grub_getrtsecs)
+       pushl   %ebp
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       clc
+       movb    $0x2, %ah
+       int     $0x1a
+
+       DATA32  jnc     gottime
+       movb    $0xff, %dh
+
+gottime:
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dh, %al
+
+       popl    %ebp
+       ret
+
+       
+/*
+ * grub_get_rtc()
+ *     return the real time in ticks, of which there are about
+ *     18-20 per second
+ */
+FUNCTION(grub_get_rtc)
+       pushl   %ebp
+
+       call    prot_to_real    /* enter real mode */
+       .code16
+
+       /* %ax is already zero */
+        int    $0x1a
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movl    %ecx, %eax
+       shll    $16, %eax
+       movw    %dx, %ax
+
+       popl    %ebp
+       ret
+
+
+/*
+ * unsigned char grub_vga_set_mode (unsigned char mode)
+ */
+FUNCTION(grub_vga_set_mode)
+       pushl   %ebp
+       pushl   %ebx
+       movl    %eax, %ecx
+
+       call    prot_to_real
+       .code16
+       /* get current mode */
+       xorw    %bx, %bx
+       movb    $0x0f, %ah
+       int     $0x10
+       movb    %al, %dl
+
+       /* set the new mode */
+       movb    %cl, %al
+       xorb    %ah, %ah
+       int     $0x10
+
+       DATA32  call    real_to_prot
+       .code32
+
+       movb    %dl, %al
+       popl    %ebx
+       popl    %ebp
+       ret
+
+
+/*
+ * unsigned char *grub_vga_get_font (void)
+ */
+FUNCTION(grub_vga_get_font)
+       pushl   %ebp
+       pushl   %ebx
+
+       call    prot_to_real
+       .code16
+       movw    $0x1130, %ax
+       movb    $0x06, %bh
+       int     $0x10
+       movw    %es, %bx
+       movw    %bp, %dx
+       DATA32  call    real_to_prot
+       .code32
+
+       movzwl  %bx, %ecx
+       shll    $4, %ecx
+       movw    %dx, %ax
+       addl    %ecx, %eax
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_controller_info (struct
grub_vbe_info_block *controller_info)
+ *
+ * Register allocations for parameters:
+ * %eax                *controller_info
+ */
+FUNCTION(grub_vbe_get_controller_info)
+       pushl   %ebp
+       pushl   %edi
+
+       movw    %ax, %di        /* Store *controller_info to %edx:%di.
*/
+       xorw    %ax, %ax
+       shrl    $4, %eax
+       mov     %eax, %edx      /* prot_to_real destroys %eax.  */
+       
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *controller_info is now on %es:%di.
*/
+       movw    $0x4f00, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
+       
+       popl    %edi
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_mode_info (grub_uint32_t mode,
+ *                                          struct
grub_vbe_mode_info_block *mode_info)
+ *
+ * Register allocations for parameters:
+ * %eax                mode
+ * %edx                *mode_info
+ */
+FUNCTION(grub_vbe_get_mode_info)
+       pushl   %ebp
+       pushl   %edi
+
+       movl    %eax, %ecx      /* Store mode number to %ecx.  */
+
+       movw    %dx, %di        /* Store *mode_info to %edx:%di.  */
+       xorw    %dx, %dx
+       shrl    $4, %edx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *mode_info is now on %es:%di.  */
+       movw    $0x4f01, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+
+       movl    %edx, %eax
+       andl    $0x0FFFF, %eax  /* Return value in %eax.  */
+
+       popl    %edi
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_mode (grub_uint32_t mode,
+ *                                     struct grub_vbe_crtc_info_block
*crtc_info)
+ *
+ * Register allocations for parameters:
+ * %eax                mode
+ * %edx                *crtc_info
+ */
+FUNCTION(grub_vbe_set_mode)
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx      /* Store mode in %ebx.  */
+
+       movw    %dx, %di        /* Store *crtc_info to %edx:%di.  */
+       xorw    %dx, %dx
+       shrl    $4, %edx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %dx, %es        /* *crtc_info is now on %es:%di.  */
+
+       movw    $0x4f02, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_mode (grub_uint32_t *mode)
+ *
+ * Register allocations for parameters:
+ * %eax                *mode
+ */
+FUNCTION(grub_vbe_get_mode)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %eax            /* Push *mode to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f03, %ax
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *mode from stack to %edi.  */
+       andl    $0xFFFF, %ebx
+       movl    %ebx, (%edi)
+
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_memory_window (grub_uint32_t window,
+ *                                              grub_uint32_t
position);
+ *
+ * Register allocations for parameters:
+ * %eax                window
+ * %edx                position
+ */
+FUNCTION(grub_vbe_set_memory_window)
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f05, %ax
+       andw    $0x00ff, %bx    /* BL = window, BH = 0, Set memory
window.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_memory_window (grub_uint32_t window,
+ *                                              grub_uint32_t
*position);
+ *
+ * Register allocations for parameters:
+ * %eax                window
+ * %edx                *position
+ */
+FUNCTION(grub_vbe_get_memory_window)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edx            /* Push *position to stack.  */
+
+       movl    %eax, %ebx      /* Store window in %ebx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f05, %ax
+       andw    $0x00ff, %bx    /* BL = window.  */
+       orw     $0x0100, %bx    /* BH = 1, Get memory window.  */
+       int     $0x10
+
+       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* pops *position from stack to %edi.
*/
+       andl    $0xFFFF, %edx
+       movl    %edx, (%edi)    /* Return position to caller.  */
+
+       movw    %bx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_scanline_length (grub_uint32_t
length)
+ *
+ * Register allocations for parameters:
+ * %eax                length
+ */
+FUNCTION(grub_vbe_set_scanline_length)
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ecx      /* Store length in %ecx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f06, %ax
+       movw    $0x0002, %bx    /* BL = 2, Set Scan Line in Bytes.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_scanline_length (grub_uint32_t
*length)
+ *
+ * Register allocations for parameters:
+ * %eax                *length
+ */
+FUNCTION(grub_vbe_get_scanline_length)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edx            /* Push *length to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f06, %ax
+       movw    $0x0001, %bx    /* BL = 1, Get Scan Line Length (in
bytes).  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *length from stack to %edi.  */
+       andl    $0xFFFF, %ebx
+       movl    %ebx, (%edi)    /* Return length to caller.  */
+
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_display_start (grub_uint32_t x,
+ *                                              grub_uint32_t y)
+ *
+ * Register allocations for parameters:
+ * %eax                x
+ * %edx                y
+ */
+FUNCTION(grub_vbe_set_display_start)
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ecx      /* Store x in %ecx.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f07, %ax
+       movw    $0x0080, %bx    /* BL = 80h, Set Display Start 
+                                  during Vertical Retrace.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_get_display_start (grub_uint32_t *x,
+ *                                              grub_uint32_t *y)
+ *
+ * Register allocations for parameters:
+ * %eax                *x
+ * %edx                *y
+ */
+FUNCTION(grub_vbe_get_display_start)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %eax            /* Push *x to stack.  */
+       pushl   %edx            /* Push *y to stack.  */
+
+       call    prot_to_real
+       .code16
+
+       movw    $0x4f07, %ax
+       movw    $0x0001, %bx    /* BL = 1, Get Display Start.  */
+       int     $0x10
+
+       movw    %ax, %bx        /* real_to_prot destroys %eax.  */
+
+       DATA32 call     real_to_prot
+       .code32
+
+       popl    %edi            /* Pops *y from stack to %edi.  */
+       andl    $0xFFFF, %edx
+       movl    %edx, (%edi)    /* Return y-position to caller.  */
+
+       popl    %edi            /* Pops *x from stack to %edi.  */
+       andl    $0xFFFF, %ecx
+       movl    %ecx, (%edi)    /* Return x-position to caller.  */
+
+       movw    %bx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret
+
+/*
+ * grub_vbe_status_t grub_vbe_set_palette_data (grub_uint32_t
color_count,
+ *                                             grub_uint32_t
start_index,
+ *                                             struct
grub_vbe_palette_data *palette_data)
+ *
+ * Register allocations for parameters:
+ * %eax                color_count
+ * %edx                start_index
+ * %ecx                *palette_data
+ */
+FUNCTION(grub_vbe_set_palette_data)
+       pushl   %ebp
+       pushl   %ebx
+
+       movl    %eax, %ebx      /* Store color_count in %ebx.  */
+
+       movw    %cx, %di        /* Store *palette_data to %ecx:%di.  */
+       xorw    %cx, %cx
+       shrl    $4, %ecx
+
+       call    prot_to_real
+       .code16
+
+       pushw   %es
+
+       movw    %cx, %es        /* *palette_data is now on %es:%di.  */
+       movw    %bx, %cx        /* color_count is now on %cx.  */
+
+       movw    $0x4f09, %ax
+       xorw    %bx, %bx        /* BL = 0, Set Palette Data.  */
+       int     $0x10
+
+       movw    %ax, %dx        /* real_to_prot destroys %eax.  */
+
+       popw    %es
+
+       DATA32 call     real_to_prot
+       .code32
+       
+       movw    %dx, %ax
+       andl    $0xFFFF, %eax   /* Return value in %eax.  */
+
+       popl    %ebx
+       popl    %ebp
+       ret

FILE symlist2.c (NEW)

        (1) This file SHOULD be generated by a tool.
            It is not, yet. 
            This is the symlist.c file that will use grub-emu. 
            It seems that some symbols known by grub are not available 
            for grub-emu. (they don't exist in the files compiled for
grub-emu)
            So for the time being, I connect these symbols to a NULL
function 
            (or variable).

            Booting OS or using VBE *may* not work as expected in
grub-emu :-)
            
diff -EbwBruN ./symlist2.c ../../src/grub-1.93/symlist2.c
--- ./symlist2.c        1970-01-01 01:00:00.000000000 +0100
+++ ../../src/grub-1.93/symlist2.c      2006-05-24 05:08:02.000000000
+0200
@@ -0,0 +1,215 @@
+/* This file is automatically generated by gensymlist.sh. DO NOT EDIT!
*/
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published
by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <include/grub/arg.h>
+#include <include/grub/boot.h>
+#include <include/grub/device.h>
+#include <include/grub/disk.h>
+#include <include/grub/dl.h>
+#include <include/grub/elf.h>
+#include <include/grub/env.h>
+#include <include/grub/err.h>
+#include <include/grub/file.h>
+#include <include/grub/fs.h>
+#include <include/grub/kernel.h>
+#include <include/grub/loader.h>
+#include <include/grub/misc.h>
+#include <include/grub/mm.h>
+#include <include/grub/net.h>
+#include <include/grub/parser.h>
+#include <include/grub/partition.h>
+#include <include/grub/pc_partition.h>
+#include <include/grub/rescue.h>
+#include <include/grub/symbol.h>
+#include <include/grub/term.h>
+#include <include/grub/types.h>
+#include <include/grub/machine/biosdisk.h>
+#include <include/grub/machine/boot.h>
+#include <include/grub/machine/console.h>
+#include <include/grub/machine/init.h>
+#include <include/grub/machine/memory.h>
+#include <include/grub/machine/loader.h>
+#include <include/grub/machine/time.h>
+#include <include/grub/machine/vga.h>
+#include <include/grub/machine/vbe.h>
+
+extern int grub_null();
+extern int grub_null_var;
+
+void
+grub_register_exported_symbols (void)
+{
+  struct symtab { const char *name; void *addr; };
+  struct symtab *p;
+  static struct symtab tab[] =
+    {
+      {"grub_device_open", grub_device_open},
+      {"grub_device_close", grub_device_close},
+      {"grub_device_iterate", grub_device_iterate},
+      {"grub_disk_dev_register", grub_disk_dev_register},
+      {"grub_disk_dev_unregister", grub_disk_dev_unregister},
+      {"grub_disk_dev_iterate", grub_disk_dev_iterate},
+      {"grub_disk_open", grub_disk_open},
+      {"grub_disk_close", grub_disk_close},
+      {"grub_disk_read", grub_disk_read},
+      {"grub_disk_write", grub_disk_write},
+      {"grub_dl_check_header", grub_dl_check_header},
+      {"grub_dl_load_file", grub_dl_load_file},
+      {"grub_dl_load", grub_dl_load},
+      {"grub_dl_unload", grub_dl_unload},
+      {"grub_dl_ref", grub_dl_ref},
+      {"grub_dl_unref", grub_dl_unref},
+      {"grub_dl_iterate", grub_dl_iterate},
+      {"grub_dl_get", grub_dl_get},
+      {"grub_dl_register_symbol", grub_dl_register_symbol},
+      {"grub_dl_resolve_symbol", grub_dl_resolve_symbol},
+      {"grub_env_set", grub_env_set},
+      {"grub_env_get", grub_env_get},
+      {"grub_env_unset", grub_env_unset},
+      {"grub_env_iterate", grub_env_iterate},
+      {"grub_register_variable_hook", grub_register_variable_hook},
+      {"grub_error", grub_error},
+      {"grub_fatal", grub_fatal},
+      {"grub_error_push", grub_error_push},
+      {"grub_error_pop", grub_error_pop},
+      {"grub_print_error", grub_print_error},
+      {"grub_file_get_device_name", grub_file_get_device_name},
+      {"grub_file_open", grub_file_open},
+      {"grub_file_read", grub_file_read},
+      {"grub_file_seek", grub_file_seek},
+      {"grub_file_close", grub_file_close},
+      {"grub_fs_register", grub_fs_register},
+      {"grub_fs_unregister", grub_fs_unregister},
+      {"grub_fs_iterate", grub_fs_iterate},
+      {"grub_fs_probe", grub_fs_probe},
+      {"grub_loader_is_loaded", grub_loader_is_loaded},
+      {"grub_loader_set", grub_loader_set},
+      {"grub_loader_unset", grub_loader_unset},
+      {"grub_loader_boot", grub_loader_boot},
+      {"grub_memmove", grub_memmove},
+      {"grub_rescue_get_command", grub_rescue_get_command},
+      {"grub_strcpy", grub_strcpy},
+      {"grub_strncpy", grub_strncpy},
+      {"grub_stpcpy", grub_stpcpy},
+      {"grub_strcat", grub_strcat},
+      {"grub_strncat", grub_strncat},
+      {"memmove", memmove},
+      {"memcpy", memcpy},
+      {"grub_memcmp", grub_memcmp},
+      {"grub_strcmp", grub_strcmp},
+      {"grub_strncmp", grub_strncmp},
+      {"grub_strncasecmp", grub_strncasecmp},
+      {"grub_strchr", grub_strchr},
+      {"grub_strrchr", grub_strrchr},
+      {"grub_strword", grub_strword},
+      {"grub_iswordseparator", grub_iswordseparator},
+      {"grub_isspace", grub_isspace},
+      {"grub_isprint", grub_isprint},
+      {"grub_isalpha", grub_isalpha},
+      {"grub_isgraph", grub_isgraph},
+      {"grub_isdigit", grub_isdigit},
+      {"grub_tolower", grub_tolower},
+      {"grub_strtoul", grub_strtoul},
+      {"grub_strdup", grub_strdup},
+      {"grub_strndup", grub_strndup},
+      {"grub_memset", grub_memset},
+      {"grub_strlen", grub_strlen},
+      {"grub_printf", grub_printf},
+      {"grub_real_dprintf", grub_real_dprintf},
+      {"grub_vprintf", grub_vprintf},
+      {"grub_sprintf", grub_sprintf},
+      {"grub_vsprintf", grub_vsprintf},
+      {"grub_stop", grub_stop},
+      {"grub_utf16_to_utf8", grub_utf16_to_utf8},
+      {"grub_utf8_to_ucs4", grub_utf8_to_ucs4},
+      {"grub_malloc", grub_malloc},
+      {"grub_mmap", grub_mmap},
+      {"grub_free", grub_free},
+      {"grub_realloc", grub_realloc},
+      {"grub_memalign", grub_memalign},
+      {"grub_parser_cmdline_state", grub_parser_cmdline_state},
+      {"grub_parser_split_cmdline", grub_parser_split_cmdline},
+      {"grub_partition_probe", grub_partition_probe},
+      {"grub_partition_iterate", grub_partition_iterate},
+      {"grub_partition_get_name", grub_partition_get_name},
+      {"grub_partition_map_iterate", grub_partition_map_iterate},
+      {"grub_partition_map_register", grub_partition_map_register},
+      {"grub_partition_map_unregister", grub_partition_map_unregister},
+      {"grub_rescue_register_command", grub_rescue_register_command},
+      {"grub_rescue_unregister_command",
grub_rescue_unregister_command},
+      {"grub_term_register", grub_term_register},
+      {"grub_term_unregister", grub_term_unregister},
+      {"grub_term_iterate", grub_term_iterate},
+      {"grub_term_set_current", grub_term_set_current},
+      {"grub_term_get_current", grub_term_get_current},
+      {"grub_putchar", grub_putchar},
+      {"grub_putcode", grub_putcode},
+      {"grub_getcharwidth", grub_getcharwidth},
+      {"grub_getkey", grub_getkey},
+      {"grub_checkkey", grub_checkkey},
+      {"grub_getwh", grub_getwh},
+      {"grub_getxy", grub_getxy},
+      {"grub_gotoxy", grub_gotoxy},
+      {"grub_cls", grub_cls},
+      {"grub_setcolorstate", grub_setcolorstate},
+      {"grub_setcolor", grub_setcolor},
+      {"grub_setcursor", grub_setcursor},
+      {"grub_getcursor", grub_getcursor},
+      {"grub_refresh", grub_refresh},
+      {"grub_set_more", grub_set_more},
+      {"grub_console_checkkey", grub_checkkey},
+      {"grub_console_getkey", grub_getkey},
+      {"grub_reboot", grub_reboot},
+      {"grub_halt", grub_halt},
+      {"grub_break", grub_break},
+      {"grub_linux_boot_zimage", grub_null},
+      {"grub_linux_boot_bzimage", grub_null},
+      {"grub_chainloader_real_boot", grub_null},
+      {"grub_multiboot_real_boot", grub_null},
+      {"grub_get_rtc", grub_get_rtc},
+      {"grub_vga_set_mode", grub_null},
+      {"grub_vga_get_font", grub_null},
+      {"grub_vbe_get_controller_info", grub_null},
+      {"grub_vbe_get_mode_info", grub_null},
+      {"grub_vbe_set_mode", grub_null},
+      {"grub_vbe_get_mode", grub_null},
+      {"grub_vbe_set_memory_window", grub_null},
+      {"grub_vbe_get_memory_window", grub_null},
+      {"grub_vbe_set_scanline_length", grub_null},
+      {"grub_vbe_get_scanline_length", grub_null},
+      {"grub_vbe_set_display_start", grub_null},
+      {"grub_vbe_get_display_start", grub_null},
+      {"grub_vbe_set_palette_data", grub_null},
+      {"grub_errno", &grub_errno},
+      {"grub_errmsg", &grub_errmsg},
+      {"grub_fs_autoload_hook", &grub_fs_autoload_hook},
+      {"grub_lower_mem", &grub_null_var},
+      {"grub_upper_mem", &grub_null_var},
+      {"grub_os_area_addr", &grub_null_var},
+      {"grub_os_area_size", &grub_null_var},
+      {"grub_linux_prot_size", &grub_null_var},
+      {"grub_linux_tmp_addr", &grub_null_var},
+      {"grub_linux_real_addr", &grub_null_var},
+      {0, 0}
+    };
+
+  for (p = tab; p->name; p++)
+    grub_dl_register_symbol (p->name, p->addr, 0);
+}


FILE util/grub-emu.c

        (1) grub_arch_dl_check_header() and
grub_arch_dl_relocate_symbols()
            are deleted from this file. grub-emu now use the same
functions
            as those used by grub (from kern/i386/dl.c).


diff -EbwBruN ./util/grub-emu.c ../../src/grub-1.93/util/grub-emu.c
--- ./util/grub-emu.c   2005-11-13 16:47:09.000000000 +0100
+++ ../../src/grub-1.93/util/grub-emu.c 2006-05-13 11:23:07.000000000
+0200
@@ -56,24 +56,7 @@
 grub_addr_t
 grub_arch_modules_addr (void)
 {
-  return 0;
-}
-
-grub_err_t
-grub_arch_dl_check_header (void *ehdr)
-{
-  (void) ehdr;
-
-  return GRUB_ERR_BAD_MODULE;
-}
-
-grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
-{
-  (void) mod;
-  (void) ehdr;
-
-  return GRUB_ERR_BAD_MODULE;
+  return 000;
 }
 
 void
@@ -200,6 +183,8 @@
 
   grub_fini_all ();
 
+  grub_dl_unload_all();
+
   grub_machine_fini ();
   
   return 0;

FILE util/misc.c

        (3) grub_mmap() (NEW). Used by one of the modules I work on.
            FIXME : heap starts at arbitary location.

        (1) grub_null() (NEW).  Template for undefined symbols in
grub-emu.
        (1) grub_null_var (NEW).  Template for undefined symbols in
grub-emu.

        (1) grub_register_export_symbols() used by grub-emu is now taken
            from kern/i386/dl.c, as for grub.

        (4) minor change in free(), though you don't care on Linux.


diff -EbwBruN ./util/misc.c ../../src/grub-1.93/util/misc.c
--- ./util/misc.c       2005-02-27 22:19:06.000000000 +0100
+++ ../../src/grub-1.93/util/misc.c     2006-05-08 23:17:40.000000000
+0200
@@ -212,10 +212,24 @@
   return xmalloc (size);
 }
 
+#include <sys/mman.h>
+
+static char * heapstart=(char*) 0x01200000;
+void *
+grub_mmap (grub_size_t size)
+{
+    void * heap;
+    heap=(void *)mmap(heapstart, size,
+               PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
-1, 0);
+    heapstart+=size; 
+    printf("Using mmap (size %d)\n", size);
+    return heap;
+}
+
 void
 grub_free (void *ptr)
 {
-  free (ptr);
+  if (ptr) free (ptr);
 }
 
 void *
@@ -244,11 +258,6 @@
 }
 
 void
-grub_register_exported_symbols (void)
-{
-}
-
-void
 grub_stop (void)
 {
   exit (1);
@@ -271,3 +280,10 @@
                       grub_size_t len __attribute__ ((unused)))
 {
 }
+
+int grub_null()
+{
+    return 0;
+}
+
+int grub_null_var = 0;


FILE genmk.rb

        (2) Add the build of .elf files for modules 
            See Lubomir Kundrak previous emails.

diff -EbwBruN ./genmk.rb ../../src/grub-1.93/genmk.rb
--- ./genmk.rb  2005-11-18 15:56:55.000000000 +0100
+++ ../../src/grub-1.93/genmk.rb        2006-05-13 19:39:34.000000000
+0200
@@ -108,18 +108,22 @@
     mod_obj = mod_src.suffix('o')
     defsym = 'def-' + @name.suffix('lst')
     undsym = 'und-' + @name.suffix('lst')
+    elf = @name.suffix('elf')
     mod_name = File.basename(@name, '.mod')
     symbolic_name = mod_name.sub(/\.[^\.]*$/, '')
     
-    "CLEANFILES += address@hidden #{mod_obj} #{mod_src} #{pre_obj}
#{objs_str} #{defsym} #{undsym}
+    "CLEANFILES += address@hidden #{mod_obj} #{mod_src} #{pre_obj}
#{objs_str} #{defsym} #{undsym} #{elf}
 MOSTLYCLEANFILES += #{deps_str}
 DEFSYMFILES += #{defsym}
 UNDSYMFILES += #{undsym}
 
address@hidden: #{pre_obj} #{mod_obj}
+#{elf}: #{pre_obj} #{mod_obj}
        -rm -f $@
        $(LD) $(#{prefix}_LDFLAGS) $(LDFLAGS) -r -d -o $@ $^
-       $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R
.note -R .comment $@
+
address@hidden: #{elf}
+       -rm -f $@
+       $(OBJCOPY) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R
.note -R .comment $^ $@
 
 #{pre_obj}: #{objs_str}
        -rm -f $@

FILE include/grub/dl.h

        (2) Add mod->seg->name[LG_SEGMENT_NAME]
            LG_SEGMENT_NAME = 30 is arbitrary. FIXME if you know a limit
            for ELF section names.

        (2) Add mod->elf so that it's easy to tell GDB a new module is
loaded
            or unloaded.

        (2) Add 6 grub_gdb..() functions to be found in dl-gdb.c to
provide
            a GDB interface when loading/unloading modules.
            (actually defined only with #define GRUB_UTIL but might be
            useful for the serial line GDB interface developped by
Lubomir),
            See the example of dl-gdb.c file I provided for Linux.

        (4) grub_mod_init() uses parameter mod. At least for the
            normal module. Seemed that it makes a difference depending
on
            compiler used.
        
diff -EbwBruN ./include/grub/dl.h ../../src/grub-1.93/include/grub/dl.h
--- ./include/grub/dl.h 2005-11-27 13:21:12.000000000 +0100
+++ ../../src/grub-1.93/include/grub/dl.h       2006-05-25
16:41:34.000000000 +0200
@@ -26,12 +26,12 @@
 #include <grub/types.h>
 
 #define GRUB_MOD_INIT(name)    \
-static void grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
__attribute__ ((used)); \
+static void grub_mod_init (grub_dl_t mod) __attribute__ ((used)); \
 void grub_##name##_init (void); \
 void \
 grub_##name##_init (void) { grub_mod_init (0); } \
 static void \
-grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
+grub_mod_init (grub_dl_t mod)
 
 #define GRUB_MOD_FINI(name)    \
 static void grub_mod_fini (void) __attribute__ ((used)); \
@@ -47,12 +47,14 @@
 #define GRUB_MOD_DEP(name)     \
 __asm__ (".section .moddeps,\"S\"\n.string \"" #name "\"\n.previous")
 
+#define LG_SEGMENT_NAME        30
 struct grub_dl_segment
 {
   struct grub_dl_segment *next;
   void *addr;
   grub_size_t size;
   unsigned section;
+  char name [LG_SEGMENT_NAME];
 };
 typedef struct grub_dl_segment *grub_dl_segment_t;
 
@@ -65,6 +67,13 @@
 };
 typedef struct grub_dl_dep *grub_dl_dep_t;
 
+#ifdef GRUB_UTIL
+#include <string.h>
+#include <stdio.h>
+#define GRUB_PREFIX_SOFILE     NULL
+#include <grub/link.h>
+#endif /* GRUB_UTIL */
+
 struct grub_dl
 {
   char *name;
@@ -73,13 +82,27 @@
   grub_dl_segment_t segment;
   void (*init) (struct grub_dl *mod);
   void (*fini) (void);
+#ifdef GRUB_UTIL
+  char elf [255];      /* builting .elf file reference */
+  char * current;      /* current position in buffer elf to write on */
+  struct link_map lm;  /* to be added to libld's link maps */
+#endif /* GRUB_UTIL */
 };
 typedef struct grub_dl *grub_dl_t;
 
+#ifdef GRUB_UTIL
+int grub_gdb_preload_mod (grub_dl_t mod, const char * filename);
+int grub_gdb_load_mod (grub_dl_t mod);
+void grub_gdb_addseg (grub_dl_t mod, char * segname,
+               ElfW(Addr) segaddr, int segsize);
+void grub_gdb_unload_mod (grub_dl_t mod);
+void grub_gdb_done (void);
+#endif
+
 grub_err_t EXPORT_FUNC(grub_dl_check_header) (void *ehdr, grub_size_t
size);
 grub_dl_t EXPORT_FUNC(grub_dl_load_file) (const char *filename);
 grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
-grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
+grub_dl_t EXPORT_FUNC(grub_dl_load_core) (void *addr, grub_size_t size,
const char * filename);
 int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
 void grub_dl_unload_unneeded (void);
 void grub_dl_unload_all (void);

FILE include/grub/link.h (NEW)

        (2) needed by dl-gdb.c

diff -EbwBruN ./include/grub/link.h
../../src/grub-1.93/include/grub/link.h
--- ./include/grub/link.h       1970-01-01 01:00:00.000000000 +0100
+++ ../../src/grub-1.93/include/grub/link.h     2006-05-13
01:02:39.000000000 +0200
@@ -0,0 +1,94 @@
+/* Data structure for communication from the run-time dynamic linker
for
+   loaded ELF shared objects.
+   Copyright (C) 1995-2001, 2004, 2005, 2006 Free Software Foundation,
Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef        _LINK_H
+#define        _LINK_H 1
+#include "elf.h"
+
+/* We use this macro to refer to ELF types independent of the native
wordsize.
+ *    `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'.
*/
+#define ElfW(type)      _ElfW (Elf, __ELF_NATIVE_CLASS, type)
+#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t)  e##w##t
+
+/* #include <bits/elfclass.h>              Defines __ELF_NATIVE_CLASS.
*/
+
+#include <bits/wordsize.h>
+#define __ELF_NATIVE_CLASS __WORDSIZE
+
+/* Rendezvous structure used by the run-time dynamic linker to
communicate
+   details of shared object loading to the debugger.  If the
executable's
+   dynamic section has a DT_DEBUG element, the run-time linker sets
that
+   element's value to the address where this structure can be found.
*/
+
+struct r_debug
+  {
+    int r_version;             /* Version number for this protocol.  */
+
+    struct link_map *r_map;    /* Head of the chain of loaded objects.
*/
+
+    /* This is the address of a function internal to the run-time
linker,
+       that will always be called when the linker begins to map in a
+       library or unmap it, and again when the mapping change is
complete.
+       The debugger can set a breakpoint at this address if it wants to
+       notice shared object mapping changes.  */
+    ElfW(Addr) r_brk;
+    enum
+      {
+       /* This state value describes the mapping change taking place
when
+          the `r_brk' address is called.  */
+       RT_CONSISTENT,          /* Mapping change is complete.  */
+       RT_ADD,                 /* Beginning to add a new object.  */
+       RT_DELETE               /* Beginning to remove an object
mapping.  */
+      } r_state;
+
+    ElfW(Addr) r_ldbase;       /* Base address the linker is loaded at.
*/
+  };
+
+/* This is the instance of that structure used by the dynamic linker.
*/
+extern struct r_debug _r_debug;
+
+/* This symbol refers to the "dynamic structure" in the `.dynamic'
section
+   of whatever module refers to `_DYNAMIC'.  So, to find its own
+   `struct r_debug', a program could do:
+     for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
+       if (dyn->d_tag == DT_DEBUG)
+        r_debug = (struct r_debug *) dyn->d_un.d_ptr;
+   */
+extern ElfW(Dyn) _DYNAMIC[];
+
+/* Structure describing a loaded shared object.  The `l_next' and
`l_prev'
+   members form a chain of all the shared objects loaded at startup.
+
+   These data structures exist in space used by the run-time dynamic
linker;
+   modifying them may have disastrous results.  */
+
+struct link_map
+  {
+    /* These first few members are part of the protocol with the
debugger.
+       This is the same format used in SVR4.  */
+
+    ElfW(Addr) l_addr;         /* Base address shared object is loaded
at.  */
+    char *l_name;              /* Absolute file name object was found
in.  */
+    ElfW(Dyn) *l_ld;           /* Dynamic section of the shared object.
*/
+    struct link_map *l_next, *l_prev; /* Chain of loaded objects.  */
+  };
+
+#endif /* link.h */

FILE kern/dl.c

        (2) Add mod->seg->name (section name)

        (2) With #define GRUB_UTIL only 
            When module is loaded, calls are made to :
                - grub_gdb_preload_mod() to init things
                - grub_gdb_add_seg() for each section
                - grub_gdb_load_mod() when module is in memory
                - grub_gdb_done() when module is ready to run
                      before (mod->init())

            When module is unloaded, calls are made to :
                - grub_gdb_unload_mod() to advertize of the change
                - grub_gdb_done() when module is unloaded
                        
        Examples for these functions are in kern/dl-gdb.c

        I am not sure of the best strategy for .elf files.
        Should they reside with the sources files where we compile grub
        or should they be in same directory of the loaded module.
        So I actually propagate filename parameter to some functions
        that don't need it in the first case.

        (4) "set debug=symbols" now prints detailled info on symbols
loaded

diff -EbwBruN ./kern/dl.c ../../src/grub-1.93/kern/dl.c
--- ./kern/dl.c 2005-12-10 06:24:59.000000000 +0100
+++ ../../src/grub-1.93/kern/dl.c       2006-05-25 19:44:50.000000000
+0200
@@ -267,6 +267,10 @@
 {
   unsigned i;
   Elf_Shdr *s;
+  char * stab; /* String table */
+
+  s = (Elf_Shdr *) ((char *) e + e->e_shoff + (e->e_shentsize *
e->e_shstrndx));
+  stab = (char *) e + s->sh_offset;
   
   for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
        i < e->e_shnum;
@@ -302,6 +306,13 @@
                }
 
              seg->addr = addr;
+             grub_strncpy (seg->name, stab + s->sh_name,
+                     min(LG_SEGMENT_NAME, grub_strlen(stab +
s->sh_name) + 1));
+             *(seg->name + LG_SEGMENT_NAME - 1) = '\0';
+#ifdef GRUB_UTIL
+             grub_gdb_addseg (mod, seg->name,
+                     seg->addr, (int) s->sh_size);
+#endif
            }
          else
            seg->addr = 0;
@@ -316,7 +327,7 @@
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
+grub_err_t
 grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
 {
   unsigned i;
@@ -359,6 +370,10 @@
              if (! sym->st_value)
                return grub_error (GRUB_ERR_BAD_MODULE,
                                   "the symbol `%s' not found", name);
+             else {
+               grub_dprintf ("symbols",
+                       "%s: Resolved Symbol %s\n", mod->name, name);
+             }
            }
          else
            sym->st_value = 0;
@@ -368,16 +383,28 @@
          sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
 
sym->st_shndx);
          if (bind != STB_LOCAL)
-           if (grub_dl_register_symbol (name, (void *) sym->st_value,
mod))
+           if (grub_dl_register_symbol (name, (void *) sym->st_value,
mod)) {
+             grub_dprintf("symbols",
+                     "Err registering %s (%d)\n", name, grub_errno);
              return grub_errno;
+           } else {
+             grub_dprintf("symbols",
+                     "%s: Registering %s\n", mod->name, name);
+           }
          break;
 
        case STT_FUNC:
          sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
 
sym->st_shndx);
          if (bind != STB_LOCAL)
-           if (grub_dl_register_symbol (name, (void *) sym->st_value,
mod))
+           if (grub_dl_register_symbol (name, (void *) sym->st_value,
mod)) {
+             grub_dprintf("symbols",
+                     "Err registering %s (%d)\n", name, grub_errno);
              return grub_errno;
+           } else {
+             grub_dprintf("symbols",
+                     "%s: Registering %s\n", mod->name, name);
+           }
          
          if (grub_strcmp (name, "grub_mod_init") == 0)
            mod->init = (void (*) (grub_dl_t)) sym->st_value;
@@ -512,6 +539,9 @@
     if (seg->size) {
       grub_dprintf ("modules", "flushing 0x%x bytes at %p\n",
seg->size,
                    seg->addr);
+      grub_dprintf("symbols", 
+             "%s: Section %d loaded at 0x%x (size 0x%x)\n",
+             mod->name, seg->section, (int) seg->addr, seg->size);
       grub_arch_sync_caches (seg->addr, seg->size);
     }
   }
@@ -519,7 +549,7 @@
 
 /* Load a module from core memory.  */
 grub_dl_t
-grub_dl_load_core (void *addr, grub_size_t size)
+grub_dl_load_core (void *addr, grub_size_t size, const char * filename)
 {
   Elf_Ehdr *e;
   grub_dl_t mod;
@@ -553,10 +583,17 @@
   mod->init = 0;
   mod->fini = 0;
 
+
   grub_dprintf ("modules", "relocating to %p\n", mod);
   if (grub_dl_resolve_name (mod, e)
+#ifdef GRUB_UTIL
+      || grub_gdb_preload_mod(mod, filename)
+#endif
       || grub_dl_resolve_dependencies (mod, e)
       || grub_dl_load_segments (mod, e)
+#ifdef GRUB_UTIL
+      || grub_gdb_load_mod(mod)
+#endif
       || grub_dl_resolve_symbols (mod, e)
       || grub_arch_dl_relocate_symbols (mod, e))
     {
@@ -567,6 +604,10 @@
 
   grub_dl_flush_cache (mod);
 
+#ifdef GRUB_UTIL 
+  grub_gdb_done();
+#endif
+
   grub_dprintf ("modules", "module name: %s\n", mod->name);
   grub_dprintf ("modules", "init function: %p\n", mod->init);
   grub_dl_call_init (mod);
@@ -601,7 +642,7 @@
   if (grub_file_read (file, core, size) != (int) size)
     goto failed;
 
-  mod = grub_dl_load_core (core, size);
+  mod = grub_dl_load_core (core, size, filename);
   if (! mod)
     goto failed;
   
@@ -660,6 +701,10 @@
   if (mod->fini)
     (mod->fini) ();
   
+#ifdef GRUB_UTIL
+  grub_gdb_unload_mod(mod);
+#endif
+
   grub_dl_remove (mod);
   grub_dl_unregister_symbols (mod);
   
@@ -680,6 +725,10 @@
       grub_free (seg);
     }
   
+#ifdef GRUB_UTIL
+  grub_gdb_done();
+#endif
+
   grub_free (mod->name);
   grub_free (mod);
   return 1;

FILE kern/dl-gdb.c (NEW)

        (2) Example of what can be done to tell GDB about
            dynamically loaded/unloaded modules

            mod->elf contains the string 
            "module.elf 0x2324 -s .text 0x2324 -s .data ..."
            when grub_gdb_load_mod() is called.

            With #define GRUB_COMPAT_GDB, you'll need to upgrade gdb 6.1
            with a modified version of solib-svr4.c
            GDB will load/unload symbols as it would for any .so
library,
            and manage pending breakpoints.
            

diff -EbwBruN ./kern/dl-gdb.c ../../src/grub-1.93/kern/dl-gdb.c
--- ./kern/dl-gdb.c     1970-01-01 01:00:00.000000000 +0100
+++ ../../src/grub-1.93/kern/dl-gdb.c   2006-05-25 17:24:36.000000000
+0200
@@ -0,0 +1,169 @@
+/* dl-gdb.c Interface between Grub and GDB for dynamically loaded
modules */
+
+#include <config.h>
+#include <grub/elf.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/symbol.h>
+#include <grub/file.h>
+#include <grub/env.h>
+#include <grub/cache.h>
+
+
+#ifdef GRUB_UTIL 
+
+#ifdef GRUB_COMPAT_GDB
+
+static struct r_debug * grub_gdb_debug_reg() {
+    static struct r_debug * r_debug = NULL;
+    ElfW(Dyn) * dyn;
+    if (r_debug == NULL) 
+       for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
+           if (dyn->d_tag == DT_DEBUG) 
+               r_debug = (struct r_debug *) dyn->d_un.d_ptr;
+       
+    return r_debug;
+}
+
+extern void _dl_debug_state(void);
+
+static void grub_gdb_debug_sig (int signal) {
+    struct r_debug * r_debug = grub_gdb_debug_reg();
+
+    if (r_debug &&
+           r_debug->r_state != signal) {
+       r_debug->r_state = signal;
+       _dl_debug_state();
+    }
+}
+
+int grub_gdb_load_mod (grub_dl_t mod) {
+    struct r_debug * r_debug = grub_gdb_debug_reg();
+    struct link_map * lm;
+    for (lm = r_debug->r_map;  /* There is already a map loaded */
+           lm->l_next; lm = lm->l_next) ;
+    lm->l_next = & mod->lm;
+    mod->lm.l_prev = lm;
+    mod->lm.l_next = NULL;
+    grub_dprintf ("symbols", "Loaded %s\n", mod->elf);
+    grub_gdb_debug_sig(RT_ADD);
+    return 0;
+}
+
+void grub_gdb_unload_mod(grub_dl_t mod) {
+    if (mod->lm.l_prev && mod->segment) {
+       mod->lm.l_prev->l_next = mod->lm.l_next;
+       if (mod->lm.l_next)
+           mod->lm.l_next->l_prev = mod->lm.l_prev;
+       mod->lm.l_prev = NULL;
+       grub_gdb_debug_sig(RT_DELETE);
+    }
+}
+
+void grub_gdb_done() {
+    grub_gdb_debug_sig(RT_CONSISTENT);
+}
+
+#else /* GRUB_COMPAT_GDB */
+
+int grub_gdb_load_mod (grub_dl_t mod) {
+    /* This function is intended to be caught by .gdbinit scripts */
+    grub_dprintf ("symbols", "Loaded %s\n", mod->elf);
+    return 0;
+}
+
+void grub_gdb_unload_mod(grub_dl_t mod) {
+    /* Don't know how to unload GDB symbols without GRUB_COMPAT_GDB */
+}
+
+void grub_gdb_done() {
+}
+
+#endif /* else GRUB_COMPAT_GDB */
+
+int grub_gdb_preload_mod (grub_dl_t mod, const char * filename) {
+  /* Compute name of .elf file for that module,
+   * to be read by GDB since module is loaded.
+   * 
+   * Suppress leading (device) part of filename
+   * (FIXME : We should be using some device map to replace (device))
+   *
+   * Add GRUB_PREFIX_SOFILE instead of directory of current filename
+   * if that symbol is defined.
+   * 
+   * Suppress .mod extension
+   * Add .elf extension
+   *
+   * Use mod->name instead of filename if that last one is NULL
+   */
+
+  char * ptr;
+  char * cur;
+  char * name;
+
+  if (mod == NULL) return 1;
+
+  mod->lm.l_addr = (ElfW(Addr)) NULL;
+  mod->lm.l_name = mod->elf;
+  mod->lm.l_ld = _DYNAMIC;
+  mod->lm.l_next = NULL;
+  mod->lm.l_prev = NULL;
+
+  name = (char *) filename;
+  if (name == NULL) name = mod->name;
+  cur = mod->elf;
+  if (GRUB_PREFIX_SOFILE) {
+      strcpy(cur, GRUB_PREFIX_SOFILE);
+      cur += strlen(cur);
+      *cur++ = '/';
+      ptr = strrchr(name, '/');
+      if (ptr)
+         ptr++;
+      else
+         ptr = name;
+      strcpy (cur, ptr);
+      cur += strlen(cur);
+      mod->current = cur;
+  } else {
+      ptr = NULL;
+      if (*name == '(') 
+         /* FIXME : We should be doing device mapping */
+         ptr = strchr(name, ')');
+      if (! ptr)
+         ptr = name;
+      else 
+         ptr++;
+      strcpy(cur, ptr);
+      cur += strlen(cur);
+  }
+  if (strcmp(cur - 4, ".mod")  == 0)
+      strcpy(cur - 4, ".elf");
+  else {
+      strcpy (cur, ".elf");
+      cur += strlen(cur);
+  }
+  mod->current = cur;
+  return 0;
+}      
+
+void grub_gdb_addseg (grub_dl_t mod, char * segname,
+               ElfW(Addr) segaddr, int segsize __attribute__
((unused))) {
+    if (mod && mod->current) {
+
+       /* .text segment is the first one we should see loaded
+        * in all ELF files */
+       if (strcmp(segname, ".text") == 0) {
+           sprintf (mod->current, " 0x%x -s %s 0x%x",
+                       segaddr, segname, segaddr);
+           mod->lm.l_addr = segaddr;
+       } else {
+           sprintf (mod->current, " -s %s 0x%x", segname, segaddr);
+       }
+       mod->current += strlen (mod->current);
+    }
+}
+
+#endif /* GRUB_UTIL */


FILE gdb-6.1/gdb/solib-svr4.c

        (2) This file is from the GDB 6.1 distribution.
            Changes made to this file allow ELF sections to be loaded at

            non-contigus memory locations (the way Grub does for
modules).

            That way, GDB don't make the difference between a .so
library
            and a Grub module being loaded when you compile with
            the -DGRUB_COMPAT_GDB option.

            Nota Bene : We could also change the way grub allocate
memory for 
            loading modules (allocate one bunch of memory for all
sections to
            be loaded), at the cost of a two-passes loop in
            grub_dl_load_segments() and gain only one call to
grub_memalign().

            That would ease a lot GRUB/GDB interface for dynamic loading
of 
            modules and Lubomir scripts.

            Unless there is a plan to allocate data sections a different
way
            of text sections to avoid execution of code in data.
            (Is that possible anyway ?).

--- solib-svr4.c        2004-02-21 19:34:45.000000000 +0100
+++ ../../gdb-6.1.mod/gdb/solib-svr4.c  2006-05-25 01:09:52.000000000
+0200
@@ -68,6 +68,11 @@
        rather than void *, so that we may use byte offsets to find the
        various fields without the need for a cast.  */
     char *lm;
+    int         nsecs;
+    struct lm_sec {
+       char name [20];
+       int  addr;
+    } tab_secs[30];
   };
 
 /* On SVR4 systems, a list of symbols in the dynamic linker where
@@ -720,6 +725,7 @@
    themselves.  The declaration of `struct so_list' says which fields
    we provide values for.  */
 
+static char *     opt_pattern = " -s ";
 static struct so_list *
 svr4_current_sos (void)
 {
@@ -739,6 +745,12 @@
        return 0;
     }
 
+  if (opt_pattern)
+    {
+      char *re_err = re_comp(opt_pattern);
+      if (re_err)
+         error ("Invalid regexp: %s", re_err);
+    }
   /* Walk the inferior's link map list, and build our list of
      `struct so_list' nodes.  */
   lm = first_link_map_member ();  
@@ -797,6 +809,59 @@
            free_so (new);
          else
            {
+             /* Check to see if name contains -s options
+              * that refers to absolute load address for specific
sections
+              *
+              * name 0x27287 -s .text 0x27287 -s .data 0x128743 ...
+              *
+              * to update tab_secs[] info
+              */
+             new->lm_info->nsecs = 0;
+             if (opt_pattern && re_exec (new->so_name))
+               {
+                 /* Scan the name and get successive section info
+                  * The scan relies on the fact there is no missing
field 
+                  * in the options scanned ...
+                  */
+                 int nsecs = 0;
+                 char * ptr;
+                 char * prev = NULL;
+                 char * pprev = NULL;
+                 char * lastchar_in_name = NULL;
+                 int state = 0;
+                 for (ptr = new->so_name; *ptr; ptr++) {
+                     if (*ptr == ' ' && state == 0) {
+                         pprev = prev;
+                         prev = ptr;
+                         state = 1;
+                         continue;
+                     }
+                     if (*ptr == ' ' && state == 1) {
+                         continue;
+                     }
+                     if (*ptr == '-' && state == 1) {
+                         state = 2;
+                         continue;
+                     }
+                     if (*ptr == 's' && state == 2) {
+                         state = 3;
+                         continue;
+                     }
+                     if (*ptr == ' ' && state == 3) {
+                         if (! lastchar_in_name) 
+                             lastchar_in_name = pprev;
+                         if (sscanf (ptr, " %s %li",
+                         new->lm_info->tab_secs[nsecs].name,
+                         &new->lm_info->tab_secs[nsecs].addr) != 2)
+                             break;
+                         nsecs++;
+                     }
+                     state = 0;
+                 }
+                 if (lastchar_in_name)
+                     *lastchar_in_name = '\0';
+                 new->lm_info->nsecs = nsecs;
+               }
              new->next = 0;
              *link_ptr = new;
              link_ptr = &new->next;
@@ -1420,8 +1485,18 @@
 svr4_relocate_section_addresses (struct so_list *so,
                                  struct section_table *sec)
 {
+  int i;
   sec->addr    = svr4_truncate_ptr (sec->addr    + LM_ADDR (so));
   sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR (so));
+  for (i = 0; i < so->lm_info->nsecs; i++) {
+      if (strcmp( so->lm_info->tab_secs[i].name,
+             sec->the_bfd_section->name) == 0) {
+         sec->endaddr -= sec->addr;
+         sec->addr = so->lm_info->tab_secs[i].addr;
+         sec->endaddr += sec->addr;
+         break;
+      }
+  }
 }
 
 
FILE kern/main.c

        (2) grub_dl_load_core() has now a "filename" parameter
            (NULL as it is not relevant in this call).

diff -EbwBruN ./kern/main.c ../../src/grub-1.93/kern/main.c
--- ./kern/main.c       2005-08-12 21:53:32.000000000 +0200
+++ ../../src/grub-1.93/kern/main.c     2006-05-13 01:08:22.000000000
+0200
@@ -49,7 +49,7 @@
        header = (struct grub_module_header *) ((char *) header +
header->size))
     {
       if (! grub_dl_load_core ((char *) header + header->offset,
-                              (header->size - header->offset)))
+                              (header->size - header->offset), NULL))
        grub_fatal ("%s", grub_errmsg);
     }
 
FILE kern/misc.c

        (3) grub_break() dummy function for debugging only. Not very
useful.

diff -EbwBruN ./kern/misc.c ../../src/grub-1.93/kern/misc.c
--- ./kern/misc.c       2005-10-28 05:14:33.000000000 +0200
+++ ../../src/grub-1.93/kern/misc.c     2006-05-02 22:45:37.000000000
+0200
@@ -875,3 +875,8 @@
 
   return p - dest;
 }
+
+extern int grub_printf (const char * fmt, ...);
+void grub_break (const char * fct, int index) {
+    grub_printf ("%s: Break reached at %d\n", fct, index);
+}

FILE kern/mm.c 

        (3) grub_mmap() (NEW), as it was needed by one of the modules 
            I work on.

diff -EbwBruN ./kern/mm.c ../../src/grub-1.93/kern/mm.c
--- ./kern/mm.c 2005-08-21 20:42:54.000000000 +0200
+++ ../../src/grub-1.93/kern/mm.c       2006-05-01 17:33:01.000000000
+0200
@@ -287,6 +287,12 @@
   return grub_memalign (0, size);
 }
 
+void *
+grub_mmap (grub_size_t size)
+{
+  return grub_memalign (0, size);
+}
+
 /* Deallocate the pointer PTR.  */
 void
 grub_free (void *ptr)


FILE kern/rescue.c 

        (3) grub_rescue_get_command() (NEW),
            as it was needed by one of the modules I work on.

diff -EbwBruN ./kern/rescue.c ../../src/grub-1.93/kern/rescue.c
--- ./kern/rescue.c     2005-10-24 12:23:46.000000000 +0200
+++ ../../src/grub-1.93/kern/rescue.c   2006-05-24 05:03:26.000000000
+0200
@@ -690,3 +690,17 @@
        }
     }
 }
+
+void * grub_rescue_get_command(const char * name) {
+      grub_rescue_command_t cmd;
+      /* Find the command and and return the function.  */
+      for (cmd = grub_rescue_command_list; cmd; cmd = cmd->next)
+       {
+         if (grub_strcmp (name, cmd->name) == 0)
+           {
+             return (cmd->func);
+             break;
+           }
+       }
+      return NULL;
+}      

FILE include/grub/misc.h

        (3) grub_break() is only a dummy for debugging. Not really
useful.
        
diff -EbwBruN ./include/grub/misc.h
../../src/grub-1.93/include/grub/misc.h
--- ./include/grub/misc.h       2005-10-24 12:23:46.000000000 +0200
+++ ../../src/grub-1.93/include/grub/misc.h     2006-05-25
16:54:31.000000000 +0200
@@ -76,4 +76,8 @@
                                             const grub_uint8_t *src,
                                             grub_size_t size);
 
+void EXPORT_FUNC(grub_break) (const char * fct, int i);
+
+#define min(a,b) (a < b ? a : b)
+#define max(a,b) (a < b ? b : a)
 #endif /* ! GRUB_MISC_HEADER */

FILE include/grub/mm.h

        (3) NEW grub_mmap() needed (mostly with GRUB_UTIL) by one of the
            module I work on.

diff -EbwBruN ./include/grub/mm.h ../../src/grub-1.93/include/grub/mm.h
--- ./include/grub/mm.h 2005-08-22 19:28:59.000000000 +0200
+++ ../../src/grub-1.93/include/grub/mm.h       2006-04-26
20:41:10.000000000 +0200
@@ -29,6 +29,7 @@
 #endif
 
 void grub_mm_init_region (void *addr, grub_size_t size);
+void *EXPORT_FUNC(grub_mmap) (grub_size_t size);
 void *EXPORT_FUNC(grub_malloc) (grub_size_t size);
 void EXPORT_FUNC(grub_free) (void *ptr);
 void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size);


FILE include/grub/rescue.h

        (3) NEW grub_rescue_get_command() gives func address of a rescue
            command found by name. Needed by one of the modules I work
on.

diff -EbwBruN ./include/grub/rescue.h
../../src/grub-1.93/include/grub/rescue.h
--- ./include/grub/rescue.h     2004-04-04 15:46:00.000000000 +0200
+++ ../../src/grub-1.93/include/grub/rescue.h   2006-05-24
04:57:20.000000000 +0200
@@ -34,4 +34,7 @@
 /* Unregister a rescue mode command.  */
 void EXPORT_FUNC(grub_rescue_unregister_command) (const char *name);
 
+/* Get a rescue mode command.  */
+void * EXPORT_FUNC(grub_rescue_get_command) (const char *name);
+
 #endif /* ! GRUB_RESCUE_HEADER */

FILE include/grub/util/misc.h

        (3) grub_break() is only a dummy for debugging. Not really
useful.

diff -EbwBruN ./include/grub/util/misc.h
../../src/grub-1.93/include/grub/util/misc.h
--- ./include/grub/util/misc.h  2005-02-15 01:07:01.000000000 +0100
+++ ../../src/grub-1.93/include/grub/util/misc.h        2006-05-02
22:43:05.000000000 +0200
@@ -45,4 +45,5 @@
 void grub_util_write_image_at (const void *img, size_t size, off_t
offset,
                               FILE *out);
 
+void grub_break (const char * fct, int index);
 #endif /* ! GRUB_UTIL_MISC_HEADER */

FILE fs/ext2.c
        (4) minor sanity change.

 # For genmoddep.
diff -EbwBruN ./fs/ext2.c ../../src/grub-1.93/fs/ext2.c
--- ./fs/ext2.c 2005-11-13 16:47:09.000000000 +0100
+++ ../../src/grub-1.93/fs/ext2.c       2006-05-01 12:44:30.000000000
+0200
@@ -171,7 +171,7 @@
 };
 
 #ifndef GRUB_UTIL
-static grub_dl_t my_mod;
+static grub_dl_t my_mod = (grub_dl_t) 0;
 #endif
 

 /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of


_________________________________________
Eric Salomé - Paris - France








reply via email to

[Prev in Thread] Current Thread [Next in Thread]