]> git.proxmox.com Git - grub2.git/commitdiff
Merge mainline into keylayouts
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 31 Aug 2010 12:03:29 +0000 (14:03 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 31 Aug 2010 12:03:29 +0000 (14:03 +0200)
37 files changed:
1  2 
Makefile.util.def
grub-core/Makefile.am
grub-core/Makefile.core.def
grub-core/bus/usb/ohci.c
grub-core/bus/usb/uhci.c
grub-core/bus/usb/usbhub.c
grub-core/bus/usb/usbtrans.c
grub-core/commands/cat.c
grub-core/commands/keylayouts.c
grub-core/commands/keystatus.c
grub-core/commands/sleep.c
grub-core/efiemu/runtime/efiemu.sh
grub-core/kern/emu/console.c
grub-core/kern/i386/loader.S
grub-core/kern/i386/pc/startup.S
grub-core/kern/mips/yeeloong/init.c
grub-core/kern/rescue_reader.c
grub-core/kern/term.c
grub-core/lib/crypto.c
grub-core/loader/i386/bsd_helper.S
grub-core/loader/i386/bsd_trampoline.S
grub-core/loader/i386/efi/linux.c
grub-core/loader/i386/ieee1275/linux.c
grub-core/loader/i386/linux_trampoline.S
grub-core/normal/auth.c
grub-core/normal/cmdline.c
grub-core/normal/main.c
grub-core/normal/menu.c
grub-core/normal/menu_entry.c
grub-core/normal/menu_text.c
grub-core/term/at_keyboard.c
grub-core/term/efi/console.c
grub-core/term/i386/pc/console.c
grub-core/term/ieee1275/ofconsole.c
grub-core/term/serial.c
grub-core/term/terminfo.c
grub-core/term/usb_keyboard.c

index 0000000000000000000000000000000000000000,6b4949fd9f9cb9a67181c74340ed05a21c8194f7..b32fe0a2c523e814eba4ad0cd7b9b0c41e8943eb
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,512 +1,527 @@@
+ AutoGen definitions Makefile.tpl;
+ library = {
+   name = libgrub.a;
+   cflags = '$(CFLAGS_GCRY)';
+   cppflags = '$(CPPFLAGS_GCRY)';
+   common_nodist = grub_script.tab.c;
+   common_nodist = grub_script.yy.c;
+   common_nodist = libgrub_a_init.c;
+   common_nodist = grub_script.yy.h;
+   common_nodist = grub_script.tab.h;
+   common = grub-core/gnulib/error.c;
+   common = grub-core/gnulib/fnmatch.c;
+   common = grub-core/gnulib/getdelim.c;
+   common = grub-core/gnulib/getline.c;
+   common = grub-core/gnulib/getopt1.c;
+   common = grub-core/gnulib/getopt.c;
+   common = grub-core/gnulib/progname.c;
+   common = util/misc.c;
+   common = grub-core/kern/misc.c;
+   common = grub-core/kern/emu/mm.c;
+   common = grub-core/kern/emu/misc.c;
+   common = grub-core/kern/emu/hostfs.c;
+   common = grub-core/kern/emu/getroot.c;
+   common = grub-core/kern/emu/hostdisk.c;
+   common = grub-core/commands/blocklist.c;
+   common = grub-core/commands/extcmd.c;
+   common = grub-core/commands/ls.c;
+   common = grub-core/disk/dmraid_nvidia.c;
+   common = grub-core/disk/host.c;
+   common = grub-core/disk/loopback.c;
+   common = grub-core/disk/lvm.c;
+   common = grub-core/disk/mdraid_linux.c;
+   common = grub-core/disk/raid5_recover.c;
+   common = grub-core/disk/raid6_recover.c;
+   common = grub-core/disk/raid.c;
+   common = grub-core/fs/affs.c;
+   common = grub-core/fs/afs_be.c;
+   common = grub-core/fs/afs.c;
+   common = grub-core/fs/befs_be.c;
+   common = grub-core/fs/befs.c;
+   common = grub-core/fs/cpio.c;
+   common = grub-core/fs/ext2.c;
+   common = grub-core/fs/fat.c;
+   common = grub-core/fs/fshelp.c;
+   common = grub-core/fs/hfs.c;
+   common = grub-core/fs/hfsplus.c;
+   common = grub-core/fs/iso9660.c;
+   common = grub-core/fs/jfs.c;
+   common = grub-core/fs/minix.c;
+   common = grub-core/fs/nilfs2.c;
+   common = grub-core/fs/ntfs.c;
+   common = grub-core/fs/ntfscomp.c;
+   common = grub-core/fs/reiserfs.c;
+   common = grub-core/fs/sfs.c;
+   common = grub-core/fs/tar.c;
+   common = grub-core/fs/udf.c;
+   common = grub-core/fs/ufs2.c;
+   common = grub-core/fs/ufs.c;
+   common = grub-core/fs/xfs.c;
+   common = grub-core/kern/command.c;
+   common = grub-core/kern/device.c;
+   common = grub-core/kern/disk.c;
+   common = grub-core/kern/env.c;
+   common = grub-core/kern/err.c;
+   common = grub-core/kern/file.c;
+   common = grub-core/kern/fs.c;
+   common = grub-core/kern/list.c;
+   common = grub-core/kern/partition.c;
+   common = grub-core/lib/arg.c;
+   common = grub-core/lib/crc.c;
+   common = grub-core/lib/crypto.c;
+   common = grub-core/lib/envblk.c;
+   common = grub-core/lib/hexdump.c;
+   common = grub-core/lib/libgcrypt-grub/cipher/sha512.c;
+   common = grub-core/lib/LzFind.c;
+   common = grub-core/lib/LzmaEnc.c;
+   common = grub-core/lib/pbkdf2.c;
+   common = grub-core/normal/datetime.c;
+   common = grub-core/normal/misc.c;
+   common = grub-core/partmap/acorn.c;
+   common = grub-core/partmap/amiga.c;
+   common = grub-core/partmap/apple.c;
+   common = grub-core/partmap/gpt.c;
+   common = grub-core/partmap/msdos.c;
+   common = grub-core/partmap/sun.c;
+   common = grub-core/script/function.c;
+   common = grub-core/script/lexer.c;
+   common = grub-core/script/main.c;
+   common = grub-core/script/script.c;
+   common = grub-core/script/argv.c;
+ };
+ program = {
+   name = grub-bin2h;
+   common = util/bin2h.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+   mansection = 1;
+ };
+ program = {
+   name = grub-mkimage;
+   mansection = 1;
+   common = util/grub-mkimage.c;
+   common = util/resolve.c;
+   extra_dist = util/grub-mkimagexx.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+   cppflags = '-DGRUB_PKGLIBROOTDIR=\"$(pkglibrootdir)\"';
+ };
+ program = {
+   name = grub-mkrelpath;
+   mansection = 1;
+   common = util/grub-mkrelpath.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+ };
+ program = {
+   name = grub-script-check;
+   mansection = 1;
+   common = util/grub-script-check.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+ };
+ program = {
+   name = grub-editenv;
+   mansection = 1;
+   common = util/grub-editenv.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+ };
+ program = {
+   name = grub-mkpasswd-pbkdf2;
+   mansection = 1;
+   common = util/grub-mkpasswd-pbkdf2.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+   cflags = '$(CFLAGS_GCRY)';
+   cppflags = '$(CPPFLAGS_GCRY)';
+ };
+ program = {
+   name = grub-macho2img;
+   mansection = 1;
+   common = util/grub-macho2img.c;
+   condition = COND_APPLE_CC;
+ };
+ program = {
+   name = grub-pe2elf;
+   mansection = 1;
+   common = util/grub-pe2elf.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL)';
+   condition = COND_GRUB_PE2ELF;
+ };
+ program = {
+   name = grub-fstest;
+   mansection = 1;
+   common = util/grub-fstest.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+   condition = COND_GRUB_FSTEST;
+ };
+ program = {
+   name = grub-mkfont;
+   mansection = 1;
+   common = util/grub-mkfont.c;
+   common = grub-core/unidata.c;
+   cflags = '$(freetype_cflags)';
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
+   ldadd = '$(freetype_libs)';
+   condition = COND_GRUB_MKFONT;
+ };
+ program = {
+   name = grub-mkdevicemap;
+   installdir = sbin;
+   mansection = 8;
+   common = util/grub-mkdevicemap.c;
+   common = util/deviceiter.c;
+   nosparc64 = util/devicemap.c;
+   sparc64_ieee1275 = util/ieee1275/ofpath.c;
+   sparc64_ieee1275 = util/ieee1275/devicemap.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL)';
+ };
+ program = {
+   name = grub-probe;
+   installdir = sbin;
+   mansection = 8;
+   common = util/grub-probe.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL)';
+ };
+ program = {
+   name = grub-setup;
+   installdir = sbin;
+   mansection = 8;
+   i386_pc = util/i386/pc/grub-setup.c;
+   i386_pc = util/raid.c;
+   i386_pc = util/lvm.c;
+   sparc64_ieee1275 = util/ieee1275/ofpath.c;
+   sparc64_ieee1275 = util/sparc64/ieee1275/grub-setup.c;
+   sparc64_ieee1275 = util/raid.c;
+   sparc64_ieee1275 = util/lvm.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL)';
+   enable = i386_pc;
+   enable = sparc64_ieee1275;
+ };
+ program = {
+   name = grub-ofpathname;
+   installdir = sbin;
+   ieee1275 = util/ieee1275/grub-ofpathname.c;
+   ieee1275 = util/ieee1275/ofpath.c;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL)';
+   enable = sparc64_ieee1275;
+ };
++program = {
++  name = grub-mklayout;
++  mansection = 1;
++
++  common = util/grub-mklayout.c;
++
++  ldadd = libgrub.a;
++  ldadd = '$(LIBINTL) $(LIBDEVMAPPER)';
++};
++
+ data = {
+   common = util/grub.d/README;
+   installdir = grubconf;
+ };
+ script = {
+   name = '00_header';
+   common = util/grub.d/00_header.in;
+   installdir = grubconf;
+ };
+ script = {
+   name = '10_windows';
+   common = util/grub.d/10_windows.in;
+   installdir = grubconf;
+   condition = COND_HOST_WINDOWS;
+ };
+ script = {
+   name = '10_hurd';
+   common = util/grub.d/10_hurd.in;
+   installdir = grubconf;
+   condition = COND_HOST_HURD;
+ };
+ script = {
+   name = '10_kfreebsd';
+   common = util/grub.d/10_kfreebsd.in;
+   installdir = grubconf;
+   condition = COND_HOST_KFREEBSD;
+ };
+ script = {
+   name = '10_netbsd';
+   common = util/grub.d/10_netbsd.in;
+   installdir = grubconf;
+   condition = COND_HOST_NETBSD;
+ };
+ script = {
+   name = '10_linux';
+   common = util/grub.d/10_linux.in;
+   installdir = grubconf;
+   condition = COND_HOST_LINUX;
+ };
+ script = {
+   name = '20_linux_xen';
+   common = util/grub.d/20_linux_xen.in;
+   installdir = grubconf;
+   condition = COND_HOST_LINUX;
+ };
+ script = {
+   name = '30_os-prober';
+   common = util/grub.d/30_os-prober.in;
+   installdir = grubconf;
+ };
+ script = {
+   name = '40_custom';
+   common = util/grub.d/40_custom.in;
+   installdir = grubconf;
+ };
+ script = {
+   name = '41_custom';
+   common = util/grub.d/41_custom.in;
+   installdir = grubconf;
+ };
+ script = {
+   mansection = 1;
+   name = grub-mkrescue;
+   x86_noieee1275 = util/grub-mkrescue.in;
+   powerpc_ieee1275 = util/powerpc/ieee1275/grub-mkrescue.in;
+   enable = i386_pc;
+   enable = x86_efi;
+   enable = i386_qemu;
+   enable = i386_multiboot;
+   enable = i386_coreboot;
+   enable = powerpc_ieee1275;
+ };
+ script = {
+   mansection = 8;
+   installdir = sbin;
+   name = grub-install;
+   mips = util/grub-install.in;
+   i386_noefi_noieee1275 = util/grub-install.in;
+   x86_efi = util/i386/efi/grub-install.in;
+   i386_ieee1275 = util/ieee1275/grub-install.in;
+   powerpc_ieee1275 = util/ieee1275/grub-install.in;
+   enable = x86;
+   enable = mips;
+   enable = powerpc_ieee1275;
+ };
+ script = {
+   name = grub-mkconfig;
+   common = util/grub-mkconfig.in;
+   mansection = 8;
+   installdir = sbin;
+ };
+ script = {
+   name = grub-set-default;
+   common = util/grub-set-default.in;
+   mansection = 8;
+   installdir = sbin;
+ };
+ script = {
+   name = grub-reboot;
+   common = util/grub-reboot.in;
+   mansection = 8;
+   installdir = sbin;
+ };
+ script = {
+   name = grub-mkconfig_lib;
+   common = util/grub-mkconfig_lib.in;
+   installdir = noinst;
+ };
+ script = {
+   name = update-grub_lib;
+   common = util/update-grub_lib.in;
+   installdir = noinst;
+ };
++script = {
++  name = grub-kbdcomp;
++  common = util/grub-kbdcomp.in;
++};
++
+ script = {
+   name = grub-shell;
+   common = tests/util/grub-shell.in;
+   installdir = noinst;
+ };
+ script = {
+   name = grub-shell-tester;
+   common = tests/util/grub-shell-tester.in;
+   installdir = noinst;
+ };
+ script = {
+   testcase;
+   name = example_scripted_test;
+   common = tests/example_scripted_test.in;
+ };
+ script = {
+   testcase;
+   name = example_grub_script_test;
+   common = tests/example_grub_script_test.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_echo1;
+   common = tests/grub_script_echo1.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_echo_keywords;
+   common = tests/grub_script_echo_keywords.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_vars1;
+   common = tests/grub_script_vars1.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_for1;
+   common = tests/grub_script_for1.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_while1;
+   common = tests/grub_script_while1.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_if;
+   common = tests/grub_script_if.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_blanklines;
+   common = tests/grub_script_blanklines.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_final_semicolon;
+   common = tests/grub_script_final_semicolon.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_dollar;
+   common = tests/grub_script_dollar.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_comments;
+   common = tests/grub_script_comments.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_functions;
+   common = tests/grub_script_functions.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_break;
+   common = tests/grub_script_break.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_continue;
+   common = tests/grub_script_continue.in;
+ };
+ script = {
+   testcase;
+   name = grub_script_shift;
+   common = tests/grub_script_shift.in;
+ };
+ program = {
+   testcase;
+   name = example_unit_test;
+   common = tests/example_unit_test.c;
+   common = tests/lib/unit_test.c;
+   common = grub-core/kern/list.c;
+   common = grub-core/kern/misc.c;
+   common = grub-core/tests/lib/test.c;
+   cflags = -Wno-format;
+   ldadd = libgrub.a;
+   ldadd = '$(LIBDEVMAPPER)';
+ };
index 0000000000000000000000000000000000000000,61a0bb35a947bf13f61d640f631600731d050682..974e25352210bc344ad12cec7479db8b327dad8b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,331 +1,332 @@@
+ AUTOMAKE_OPTIONS = subdir-objects
+ DEPDIR=.deps-core
+ include $(top_srcdir)/conf/Makefile.common
+ CC=$(TARGET_CC)
+ CPP=$(TARGET_CC)
+ CCAS=$(TARGET_CC)
+ if COND_GRUB_MKFONT
+ if COND_HAVE_FONT_SOURCE
+ TARGET_CFLAGS += -DUSE_ASCII_FAILBACK=1 -DHAVE_UNIFONT_WIDTHSPEC=1
+ endif
+ endif
+ AM_CFLAGS = $(TARGET_CFLAGS)
+ AM_LDFLAGS = $(TARGET_LDFLAGS)
+ AM_CPPFLAGS = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT)
+ AM_CCASFLAGS = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT)
+ CFLAGS_PROGRAM += $(CFLAGS_PLATFORM)
+ LDFLAGS_PROGRAM += $(LDFLAGS_PLATFORM)
+ CPPFLAGS_PROGRAM += $(CPPFLAGS_PLATFORM)
+ CCASFLAGS_PROGRAM += $(CCASFLAGS_PLATFORM)
+ CFLAGS_LIBRARY += $(CFLAGS_PLATFORM) -fno-builtin
+ CPPFLAGS_LIBRARY += $(CPPFLAGS_PLATFORM)
+ CCASFLAGS_LIBRARY += $(CCASFLAGS_PLATFORM)
+ # gentrigtables
+ gentrigtables: gentrigtables.c
+       $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(CPPFLAGS) -lm $<
+ CLEANFILES += gentrigtables
+ # trigtables.c
+ trigtables.c: gentrigtables gentrigtables.c $(top_srcdir)/configure.ac
+       $(builddir)/gentrigtables > $@
+ CLEANFILES += trigtables.c
+ # XXX Use Automake's LEX & YACC support
+ grub_script.tab.h: script/parser.y
+       $(YACC) -d -p grub_script_yy -b grub_script $<
+ grub_script.tab.c: grub_script.tab.h
+ CLEANFILES += grub_script.tab.c grub_script.tab.h
+ # For the lexer.
+ grub_script.yy.h: script/yylex.l
+       $(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $<
+ grub_script.yy.c: grub_script.yy.h
+ CLEANFILES += grub_script.yy.c grub_script.yy.h
+ include $(srcdir)/Makefile.core.am
+ include $(srcdir)/Makefile.gcry.am
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/disk.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/dl.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/elf.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/elfload.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env_private.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/err.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/file.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fs.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i18n.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/kernel.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/list.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/misc.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/reader.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/symbol.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/types.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/boot.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/msdos_partition.h
+ if COND_i386_pc
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/biosdisk.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/boot.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/console.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/loader.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/vga.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/vbe.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_i386_efi
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/time.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_i386_coreboot
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/boot.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/console.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/init.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_i386_multiboot
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/boot.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/console.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/init.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_i386_qemu
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/boot.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/console.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/init.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_i386_ieee1275
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/msdos_partition.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
+ endif
+ if COND_x86_64_efi
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/time.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h
+ endif
+ if COND_mips_yeeloong
++KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/cpu/cache.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bitmap.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gfxterm.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/font.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bitmap_scale.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bufio.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libgcc.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cs5536.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pci.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/serial.h
+ endif
+ if COND_powerpc_ieee1275
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libgcc.h
+ endif
+ if COND_sparc64_ieee1275
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libgcc.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sparc64/ieee1275/ieee1275.h
+ endif
+ if COND_emu
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/cpu/time.h
+ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/cpu/types.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gzio.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/menu.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/datetime.h
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/misc.h
+ if COND_GRUB_EMU_SDL
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h
+ endif
+ if COND_GRUB_EMU_USB
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libusb.h
+ endif
+ if COND_GRUB_EMU_PCI
+ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libpciaccess.h
+ endif
+ endif
+ symlist.h: $(top_builddir)/config.h $(KERNEL_HEADER_FILES)
+       @list='$^'; \
+       for p in $$list; do \
+         echo "#include <$$p>" >> $@ || (rm -f $@; exit 1); \
+       done
+ CLEANFILES += symlist.h
+ BUILT_SOURCES += symlist.h
+ symlist.c: symlist.h gensymlist.sh
+       $(TARGET_CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) $(CPPFLAGS) -DGRUB_SYMBOL_GENERATOR=1 symlist.h > symlist.p || (rm -f symlist.p; exit 1)
+       cat symlist.p | /bin/sh $(srcdir)/gensymlist.sh $(top_builddir)/config.h $(KERNEL_HEADER_FILES) >$@ || (rm -f $@; exit 1)
+       rm -f symlist.p
+ CLEANFILES += symlist.c
+ BUILT_SOURCES += symlist.c
+ noinst_DATA += kernel_syms.lst
+ kernel_syms.lst: $(KERNEL_HEADER_FILES) $(top_builddir)/config.h
+       $(TARGET_CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) $(CPPFLAGS) $(CFLAGS) -DGRUB_SYMBOL_GENERATOR=1 $^ >kernel_syms.input
+       if grep "^#define HAVE_ASM_USCORE" $(top_builddir)/config.h; then u="_"; else u=""; fi; \
+       cat kernel_syms.input | grep -v '^#' | sed -n \
+         -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/'"$$u"'\1 kernel/;p;}' \
+         -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/'"$$u"'\1 kernel/;p;}' \
+         | sort -u >$@
+       rm -f kernel_syms.input
+ CLEANFILES += kernel_syms.lst
+ if COND_emu
+ kern/emu/grub_emu-main.$(OBJEXT):grub_emu_init.h
+ grub_emu-grub_emu_init.$(OBJEXT):grub_emu_init.h
+ kern/emu/grub_emu_dyn-main.$(OBJEXT):grub_emu_init.h
+ grub_emu_dyn-grub_emu_init.$(OBJEXT):grub_emu_init.h
+ grub_emu_init.h: genemuinitheader.sh $(MOD_FILES)
+       rm -f $@; echo $(MOD_FILES) | sh $(srcdir)/genemuinitheader.sh $(NM) > $@
+ CLEANFILES += grub_emu_init.h
+ grub_emu_init.c: grub_emu_init.h genemuinit.sh $(MOD_FILES)
+       rm -f $@; echo $(MOD_FILES) | sh $(srcdir)/genemuinit.sh $(NM) > $@
+ CLEANFILES += grub_emu_init.c
+ endif
+ # .lst files
+ platform_DATA += moddep.lst
+ platform_DATA += fs.lst
+ platform_DATA += command.lst
+ platform_DATA += partmap.lst
+ platform_DATA += handler.lst
+ platform_DATA += terminal.lst
+ platform_DATA += parttool.lst
+ platform_DATA += video.lst
+ platform_DATA += crypto.lst
+ CLEANFILES += moddep.lst
+ CLEANFILES += handler.lst
+ CLEANFILES += terminal.lst
+ CLEANFILES += parttool.lst
+ CLEANFILES += video.lst
+ CLEANFILES += crypto.lst
+ fs.lst: $(FS_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += fs.lst
+ command.lst: $(COMMAND_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += command.lst
+ partmap.lst: $(PARTMAP_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += partmap.lst
+ handler.lst: $(HANDLER_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += handler.lst
+ terminal.lst: $(TERMINAL_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += terminal.lst
+ parttool.lst: $(PARTTOOL_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += parttool.lst
+ video.lst: $(VIDEO_FILES)
+       cat $^ /dev/null | sort | uniq > $@
+ CLEANFILES += video.lst
+ # but, crypto.lst is simply copied
+ crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst
+       cp $^ $@
+ CLEANFILES += crypto.lst
+ # generate global module dependencies list
+ moddep.lst: kernel_syms.lst genmoddep.awk $(DEF_FILES) $(UND_FILES)
+       cat $(DEF_FILES) kernel_syms.lst /dev/null \
+         | $(AWK) -f $(srcdir)/genmoddep.awk $(UND_FILES) > $@ \
+         || (rm -f $@; exit 1)
+ if COND_ENABLE_EFIEMU
+ efiemu32.o: efiemu/runtime/efiemu.c $(TARGET_OBJ2ELF)
+       -rm -f $@; \
+       if test "x$(TARGET_APPLE_CC)" = x1; then \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF32 -DAPPLE_CC -m32 -Wall -Werror -nostdlib -O2 -c -o $@.bin $< || exit 1; \
+         $(OBJCONV) -felf32 -nu -nd $@.bin $@ || exit 1; \
+         rm -f $@.bin; \
+       else \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF32 -m32 -Wall -Werror -nostdlib -O2 -c -o $@ $< || exit 1; \
+         if test ! -z "$(TARGET_OBJ2ELF)"; then $(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi; \
+       fi
+ efiemu64_c.o: efiemu/runtime/efiemu.c
+       if test "x$(TARGET_APPLE_CC)" = x1; then \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF64 -DAPPLE_CC=1 -m64 -nostdlib -Wall -Werror -mno-red-zone -c -o $@ $< || exit 1; \
+       else \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF64 -m64 -nostdlib -Wall -Werror -O2 -mcmodel=large -mno-red-zone -c -o $@ $< || exit 1; \
+       fi
+ efiemu64_s.o: efiemu/runtime/efiemu.S
+       -rm -f $@
+       if test "x$(TARGET_APPLE_CC)" = x1; then \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF64 -DAPPLE_CC=1 -m64 -Wall -Werror -nostdlib -O2 -mno-red-zone -c -o $@ $< || exit 1; \
+       else \
+         $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -DELF64 -m64 -Wall -Werror -nostdlib -O2 -mcmodel=large -mno-red-zone -c -o $@ $< || exit 1; \
+       fi
+ efiemu64.o: efiemu64_c.o efiemu64_s.o $(TARGET_OBJ2ELEF)
+       -rm -f $@; \
+       if test "x$(TARGET_APPLE_CC)" = x1; then \
+         rm -f $@.bin; \
+         $(TARGET_CC) -m64 -Wl,-r -nostdlib -o $@.bin $^ || exit 1; \
+         $(OBJCONV) -felf64 -nu -nd $@.bin $@ || exit 1; \
+         rm -f $@.bin; \
+       else \
+         $(TARGET_CC) -m64 -nostdlib -Wl,-r -o $@ $^ || exit 1; \
+         if test ! -z "$(TARGET_OBJ2ELF)"; then $(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi; \
+       fi
+ platform_DATA += efiemu32.o efiemu64.o
+ CLEANFILES += efiemu32.o efiemu64.o efiemu64_c.o efiemu64_s.o
+ endif
index 0000000000000000000000000000000000000000,74fd1d3acb2864234647cd3145c7b81f07a8399b..992f4dfc99c5a91caec9961584d32ef9d8d727d0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1421 +1,1431 @@@
+ AutoGen definitions Makefile.tpl;
+ kernel = {
+   name = kernel;
+   nostrip = emu;
+   emu_ldflags              = '-Wl,-r,-d';
+   x86_efi_ldflags          = '-Wl,-r,-d';
+   x86_efi_stripflags       = '--strip-unneeded -K start -R .note -R .comment';
+   i386_pc_ldflags          = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags          = '$(TARGET_IMG_BASE_LDOPT),$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   i386_qemu_ldflags        = '$(TARGET_IMG_LDFLAGS)';
+   i386_qemu_ldflags        = '$(TARGET_IMG_BASE_LDOPT),$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   i386_coreboot_ldflags    = '-Wl,-Ttext=$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   i386_multiboot_ldflags   = '-Wl,-Ttext=$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   i386_ieee1275_ldflags    = '-Wl,-Ttext=$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   mips_yeeloong_ldflags    = '-Wl,-Ttext,$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   powerpc_ieee1275_ldflags = '-Wl,-Ttext,$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   mips_yeeloong_cppflags = '-DUSE_ASCII_FAILBACK';
+   i386_qemu_cppflags     = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   i386_qemu_ccasflags    = '-DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR)';
+   emu_cflags = '$(CFLAGS_GNULIB)';
+   emu_cppflags = '$(CPPFLAGS_GNULIB)';
+   mips_ldadd = '-lgcc';
+   powerpc_ldadd = '-lgcc';
+   sparc64_ldadd = '-lgcc';
+   i386_pc_startup = kern/i386/pc/startup.S;
+   i386_efi_startup = kern/i386/efi/startup.S;
+   x86_64_efi_startup = kern/x86_64/efi/startup.S;
+   i386_qemu_startup = kern/i386/qemu/startup.S;
+   i386_ieee1275_startup = kern/i386/ieee1275/startup.S;
+   i386_coreboot_multiboot_startup = kern/i386/coreboot/startup.S;
+   mips_yeeloong_startup = kern/mips/startup.S;
+   sparc64_ieee1275_startup = kern/sparc64/ieee1275/crt0.S;
+   powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S;
+   common = kern/command.c;
+   common = kern/corecmd.c;
+   common = kern/device.c;
+   common = kern/disk.c;
+   common = kern/dl.c;
+   common = kern/env.c;
+   common = kern/err.c;
+   common = kern/file.c;
+   common = kern/fs.c;
+   common = kern/list.c;
+   common = kern/main.c;
+   common = kern/misc.c;
+   common = kern/parser.c;
+   common = kern/partition.c;
+   common = kern/rescue_parser.c;
+   common = kern/rescue_reader.c;
+   common = kern/term.c;
+   noemu = kern/mm.c;
+   noemu = kern/time.c;
+   noemu = kern/generic/millisleep.c;
+   noemu_nodist = symlist.c;
+   noemu_noieee1275 = kern/generic/rtc_get_time_ms.c;
+   ieee1275 = disk/ieee1275/ofdisk.c;
+   ieee1275 = kern/ieee1275/cmain.c;
+   ieee1275 = kern/ieee1275/ieee1275.c;
+   ieee1275 = kern/ieee1275/mmap.c;
+   ieee1275 = kern/ieee1275/openfw.c;
+   ieee1275 = term/ieee1275/ofconsole.c;
+   ieee1275_mips = term/terminfo.c;
+   ieee1275_mips = term/tparm.c;
+   i386 = kern/i386/dl.c;
+   i386_coreboot_multiboot_qemu = kern/i386/coreboot/init.c;
+   i386_coreboot_multiboot_qemu = kern/i386/halt.c;
+   i386_coreboot_multiboot_qemu = term/i386/pc/vga_text.c;
+   i386_pc_coreboot_multiboot_qemu = term/i386/vga_common.c;
+   i386_noefi = kern/i386/misc.S;
+   x86_noieee1275 = kern/i386/pit.c;
+   x86_efi = disk/efi/efidisk.c;
+   x86_efi = kern/efi/efi.c;
+   x86_efi = kern/efi/init.c;
+   x86_efi = kern/efi/mm.c;
+   x86_efi = kern/i386/efi/init.c;
+   x86_efi = term/efi/console.c;
+   i386_efi = kern/i386/tsc.c;
+   x86_64_efi = kern/i386/tsc.c;
+   x86_64_efi = kern/x86_64/dl.c;
+   x86_64_efi = kern/x86_64/efi/callwrap.S;
+   i386_pc = kern/i386/pc/init.c;
+   i386_pc = kern/i386/pc/mmap.c;
+   i386_pc = kern/i386/tsc.c;
+   i386_pc = term/i386/pc/console.c;
+   i386_qemu = bus/pci.c;
+   i386_qemu = kern/i386/qemu/init.c;
+   i386_qemu = kern/i386/qemu/mmap.c;
+   i386_qemu = kern/i386/tsc.c;
+   i386_coreboot = kern/i386/coreboot/mmap.c;
+   i386_coreboot = kern/i386/tsc.c;
+   i386_multiboot = kern/i386/multiboot_mmap.c;
+   i386_multiboot = kern/i386/tsc.c;
+   i386_ieee1275 = kern/i386/ieee1275/init.c;
+   i386_ieee1275 = kern/ieee1275/init.c;
+   mips_yeeloong = term/ns8250.c;
+   mips_yeeloong = bus/bonito.c;
+   mips_yeeloong = bus/cs5536.c;
+   mips_yeeloong = bus/pci.c;
+   mips_yeeloong = commands/extcmd.c;
+   mips_yeeloong = font/font.c;
+   mips_yeeloong = font/font_cmd.c;
+   mips_yeeloong = io/bufio.c;
+   mips_yeeloong = kern/mips/cache.S;
+   mips_yeeloong = kern/mips/dl.c;
+   mips_yeeloong = kern/mips/init.c;
+   mips_yeeloong = kern/mips/yeeloong/init.c;
+   mips_yeeloong = lib/arg.c;
+   mips_yeeloong = term/at_keyboard.c;
+   mips_yeeloong = term/gfxterm.c;
+   mips_yeeloong = term/serial.c;
+   mips_yeeloong = video/bitmap.c;
+   mips_yeeloong = video/bitmap_scale.c;
+   mips_yeeloong = video/fb/fbblit.c;
+   mips_yeeloong = video/fb/fbfill.c;
+   mips_yeeloong = video/fb/fbutil.c;
+   mips_yeeloong = video/fb/video_fb.c;
+   mips_yeeloong = video/sm712.c;
+   mips_yeeloong = video/video.c;
++  mips_yeeloong = commands/keylayouts.c;
+   powerpc_ieee1275 = kern/ieee1275/init.c;
+   powerpc_ieee1275 = kern/powerpc/cache.S;
+   powerpc_ieee1275 = kern/powerpc/dl.c;
+   sparc64_ieee1275 = kern/sparc64/cache.S;
+   sparc64_ieee1275 = kern/sparc64/dl.c;
+   sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c;
+   sparc64_ieee1275 = kern/sparc64/ieee1275/init.c;
+   emu = disk/host.c;
+   emu = gnulib/progname.c;
+   emu = kern/emu/console.c;
+   emu = kern/emu/getroot.c;
+   emu = kern/emu/hostdisk.c;
+   emu = kern/emu/hostfs.c;
+   emu = kern/emu/main.c;
+   emu = kern/emu/misc.c;
+   emu = kern/emu/mm.c;
+   emu = kern/emu/time.c;
+   extra_dist = kern/i386/realmode.S;
+   extra_dist = kern/i386/pc/lzma_decode.S;
+   extra_dist = kern/mips/cache_flush.S;
+ };
+ program = {
+   name = grub-emu;
+   mansection = 1;
+   emu = kern/emu/full.c;
+   emu_nodist = grub_emu_init.c;
+   ldadd = 'kernel.img$(EXEEXT)';
+   ldadd = '$(MODULE_FILES)';
+   ldadd = '$(LIBUTIL) $(LIBCURSES) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER)';
+   enable = emu;
+ };
+ program = {
+   name = grub-emu-lite;
+   emu = kern/emu/lite.c;
+   emu = kern/emu/cache.S;
+   emu_nodist = symlist.c;
+   ldadd = 'kernel.img$(EXEEXT)';
+   ldadd = '$(LIBUTIL) $(LIBCURSES) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER)';
+   enable = emu;
+ };
+ image = {
+   name = boot;
+   i386_pc = boot/i386/pc/boot.S;
+   i386_qemu = boot/i386/qemu/boot.S;
+   sparc64_ieee1275 = boot/sparc64/ieee1275/boot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   i386_qemu_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_qemu_ldflags = '$(TARGET_IMG_BASE_LDOPT),$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   i386_qemu_ccasflags = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   sparc64_ieee1275_objcopyflags = '-O a.out-sunos-big';
+   sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x4000';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+   enable = i386_qemu;
+   enable = sparc64_ieee1275;
+ };
+ image = {
+   name = cdboot;
+   i386_pc = boot/i386/pc/cdboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = pxeboot;
+   i386_pc = boot/i386/pc/pxeboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = diskboot;
+   i386_pc = boot/i386/pc/diskboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x8000';
+   sparc64_ieee1275 = boot/sparc64/ieee1275/diskboot.S;
+   sparc64_ieee1275_ldflags = '-Wl,-Ttext=0x4200';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+   enable = sparc64_ieee1275;
+ };
+ image = {
+   name = lnxboot;
+   i386_pc = boot/i386/pc/lnxboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x6000';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = fwstart;
+   mips_yeeloong = boot/mips/yeeloong/fwstart.S;
+   objcopyflags = '-O binary';
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = trig;
+   common_nodist = trigtables.c;
+   extra_dist = gentrigtables.c;
+ };
+ module = {
+   name = cs5536;
+   x86 = bus/cs5536.c;
+   enable = x86;
+ };
+ module = {
+   name = libusb;
+   emu = bus/usb/emu/usb.c;
+   enable = emu;
+   condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = lsspd;
+   mips_yeeloong = commands/mips/yeeloong/lsspd.c;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = usb;
+   common = bus/usb/usb.c;
+   noemu = bus/usb/usbtrans.c;
+   noemu = bus/usb/usbhub.c;
+   enable = emu;
+   enable = i386;
+   enable = mips_yeeloong;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = usbserial_common;
+   common = bus/usb/serial/common.c;
+   enable = emu;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = usbserial_pl2303;
+   common = bus/usb/serial/pl2303.c;
+   enable = emu;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = usbserial_ftdi;
+   common = bus/usb/serial/ftdi.c;
+   enable = emu;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = uhci;
+   common = bus/usb/uhci.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = ohci;
+   common = bus/usb/ohci.c;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = pci;
+   noemu = bus/pci.c;
+   emu = bus/emu/pci.c;
+   emu = commands/lspci.c;
+   enable = emu;
+   enable = i386_pc;
+   enable = x86_efi;
+   enable = i386_ieee1275;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+   emu_condition = COND_GRUB_EMU_PCI;
+ };
+ library = {
+   name = libgnulib.a;
+   common = gnulib/regex.c;
+   extra_dist = gnulib/regcomp.c;
+   extra_dist = gnulib/regexec.c;
+   extra_dist = gnulib/fnmatch_loop.c;
+   extra_dist = gnulib/regex_internal.c;
+   cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+ };
+ module = {
+   name = cmostest;
+   i386 = commands/i386/cmostest.c;
+   enable = i386_pc;
+   enable = i386_coreboot;
+ };
+ module = {
+   name = iorw;
+   common = commands/iorw.c;
+   enable = x86;
+ };
+ module = {
+   name = regexp;
+   common = commands/regexp.c;
+   ldadd = libgnulib.a;
+   cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+ };
+ module = {
+   name = acpi;
+   i386 = commands/acpi.c;
+   x86_efi = commands/efi/acpi.c;
+   i386_pc = commands/i386/pc/acpi.c;
+   i386_coreboot = commands/i386/pc/acpi.c;
+   i386_multiboot = commands/i386/pc/acpi.c;
+   enable = x86_efi;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+ };
+ module = {
+   name = blocklist;
+   common = commands/blocklist.c;
+ };
+ module = {
+   name = boot;
+   common = commands/boot.c;
+   i386_pc = lib/i386/pc/biosnum.c;
+ };
+ module = {
+   name = cat;
+   common = commands/cat.c;
+ };
+ module = {
+   name = cmp;
+   common = commands/cmp.c;
+ };
+ module = {
+   name = configfile;
+   common = commands/configfile.c;
+ };
+ module = {
+   name = cpuid;
+   x86 = commands/i386/cpuid.c;
+   enable = x86;
+ };
+ module = {
+   name = crc;
+   common = commands/crc.c;
+   common = lib/crc.c;
+ };
+ module = {
+   name = date;
+   common = commands/date.c;
+ };
+ module = {
+   name = drivemap;
+   i386_pc = commands/i386/pc/drivemap.c;
+   i386_pc = commands/i386/pc/drivemap_int13h.S;
+   enable = i386_pc;
+ };
+ module = {
+   name = echo;
+   common = commands/echo.c;
+ };
+ module = {
+   name = extcmd;
+   common = commands/extcmd.c;
+   common = lib/arg.c;
+ };
+ module = {
+   name = fixvideo;
+   x86_efi = commands/efi/fixvideo.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = gptsync;
+   common = commands/gptsync.c;
+ };
+ module = {
+   name = halt;
+   nopc = commands/halt.c;
+   i386_pc = commands/i386/pc/halt.c;
+ };
+ module = {
+   name = hashsum;
+   common = commands/hashsum.c;
+ };
+ module = {
+   name = hdparm;
+   common = commands/hdparm.c;
+   common = lib/hexdump.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = help;
+   common = commands/help.c;
+ };
+ module = {
+   name = hexdump;
+   common = commands/hexdump.c;
+   common = lib/hexdump.c;
+ };
+ module = {
+   name = keystatus;
+   common = commands/keystatus.c;
+ };
+ module = {
+   name = loadbios;
+   x86_efi = commands/efi/loadbios.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = loadenv;
+   common = commands/loadenv.c;
+   common = lib/envblk.c;
+ };
+ module = {
+   name = ls;
+   common = commands/ls.c;
+ };
+ module = {
+   name = lsmmap;
+   common = commands/lsmmap.c;
+ };
+ module = {
+   name = lspci;
+   common = commands/lspci.c;
+   enable = x86;
+   enable = mips;
+ };
+ module = {
+   name = memrw;
+   common = commands/memrw.c;
+ };
+ module = {
+   name = minicmd;
+   common = commands/minicmd.c;
+ };
+ module = {
+   name = parttool;
+   common = commands/parttool.c;
+ };
+ module = {
+   name = password;
+   common = commands/password.c;
+ };
+ module = {
+   name = password_pbkdf2;
+   common = commands/password_pbkdf2.c;
+ };
+ module = {
+   name = play;
+   x86 = commands/i386/pc/play.c;
+   enable = x86;
+ };
+ module = {
+   name = probe;
+   common = commands/probe.c;
+ };
+ module = {
+   name = pxecmd;
+   i386_pc = commands/i386/pc/pxecmd.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = read;
+   common = commands/read.c;
+ };
+ module = {
+   name = reboot;
+   common = commands/reboot.c;
+ };
+ module = {
+   name = search;
+   common = commands/search_wrap.c;
+   extra_dist = commands/search.c;
+ };
+ module = {
+   name = search_fs_file;
+   common = commands/search_file.c;
+ };
+ module = {
+   name = search_fs_uuid;
+   common = commands/search_uuid.c;
+ };
+ module = {
+   name = search_label;
+   common = commands/search_label.c;
+ };
+ module = {
+   name = setpci;
+   common = commands/setpci.c;
+   enable = x86;
+ };
+ module = {
+   name = sleep;
+   common = commands/sleep.c;
+ };
+ module = {
+   name = suspend;
+   ieee1275 = commands/ieee1275/suspend.c;
+   enable = i386_ieee1275;
+   enable = powerpc_ieee1275;
+ };
+ module = {
+   name = terminal;
+   common = commands/terminal.c;
+ };
+ module = {
+   name = test;
+   common = commands/test.c;
+ };
+ module = {
+   name = true;
+   common = commands/true.c;
+ };
+ module = {
+   name = usbtest;
+   common = commands/usbtest.c;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+   enable = emu;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = vbeinfo;
+   i386_pc = commands/i386/pc/vbeinfo.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = vbetest;
+   i386_pc = commands/i386/pc/vbetest.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = videotest;
+   common = commands/videotest.c;
+ };
+ module = {
+   name = xnu_uuid;
+   common = commands/xnu_uuid.c;
+ };
+ module = {
+   name = dm_nv;
+   common = disk/dmraid_nvidia.c;
+ };
+ module = {
+   name = loopback;
+   common = disk/loopback.c;
+ };
+ module = {
+   name = lvm;
+   common = disk/lvm.c;
+ };
+ module = {
+   name = mdraid;
+   common = disk/mdraid_linux.c;
+ };
+ module = {
+   name = raid;
+   common = disk/raid.c;
+ };
+ module = {
+   name = raid5rec;
+   common = disk/raid5_recover.c;
+ };
+ module = {
+   name = raid6rec;
+   common = disk/raid6_recover.c;
+ };
+ module = {
+   name = scsi;
+   common = disk/scsi.c;
+ };
+ module = {
+   name = memdisk;
+   common = disk/memdisk.c;
+ };
+ module = {
+   name = ata;
+   common = disk/ata.c;
+   enable = x86;
+   enable = mips;
+ };
+ module = {
+   name = ata_pthru;
+   common = disk/ata_pthru.c;
+   enable = x86;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = biosdisk;
+   i386_pc = disk/i386/pc/biosdisk.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = usbms;
+   common = disk/usbms.c;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+   enable = emu;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = nand;
+   ieee1275 = disk/ieee1275/nand.c;
+   enable = i386_ieee1275;
+ };
+ module = {
+   name = efiemu;
+   common = efiemu/main.c;
+   common = efiemu/i386/loadcore32.c;
+   common = efiemu/i386/loadcore64.c;
+   i386_pc = efiemu/i386/pc/cfgtables.c;
+   i386_coreboot = efiemu/i386/pc/cfgtables.c;
+   i386_multiboot = efiemu/i386/pc/cfgtables.c;
+   i386_ieee1275 = efiemu/i386/nocfgtables.c;
+   i386_qemu = efiemu/i386/nocfgtables.c;
+   common = efiemu/mm.c;
+   common = efiemu/loadcore_common.c;
+   common = efiemu/symbols.c;
+   common = efiemu/loadcore32.c;
+   common = efiemu/loadcore64.c;
+   common = efiemu/prepare32.c;
+   common = efiemu/prepare64.c;
+   common = efiemu/pnvram.c;
+   common = efiemu/i386/coredetect.c;
+   extra_dist = efiemu/prepare.c;
+   extra_dist = efiemu/loadcore.c;
+   extra_dist = efiemu/runtime/efiemu.S;
+   extra_dist = efiemu/runtime/efiemu.c;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_ieee1275;
+   enable = i386_multiboot;
+   enable = i386_qemu;
+ };
+ module = {
+   name = font;
+   common = font/font.c;
+   common = font/font_cmd.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = affs;
+   common = fs/affs.c;
+ };
+ module = {
+   name = afs;
+   common = fs/afs.c;
+ };
+ module = {
+   name = afs_be;
+   common = fs/afs_be.c;
+ };
+ module = {
+   name = befs;
+   common = fs/befs.c;
+ };
+ module = {
+   name = befs_be;
+   common = fs/befs_be.c;
+ };
+ module = {
+   name = cpio;
+   common = fs/cpio.c;
+ };
+ module = {
+   name = ext2;
+   common = fs/ext2.c;
+ };
+ module = {
+   name = fat;
+   common = fs/fat.c;
+ };
+ module = {
+   name = fshelp;
+   common = fs/fshelp.c;
+ };
+ module = {
+   name = hfs;
+   common = fs/hfs.c;
+ };
+ module = {
+   name = hfsplus;
+   common = fs/hfsplus.c;
+ };
+ module = {
+   name = iso9660;
+   common = fs/iso9660.c;
+ };
+ module = {
+   name = jfs;
+   common = fs/jfs.c;
+ };
+ module = {
+   name = minix;
+   common = fs/minix.c;
+ };
+ module = {
+   name = nilfs2;
+   common = fs/nilfs2.c;
+ };
+ module = {
+   name = ntfs;
+   common = fs/ntfs.c;
+ };
+ module = {
+   name = ntfscomp;
+   common = fs/ntfscomp.c;
+ };
+ module = {
+   name = reiserfs;
+   common = fs/reiserfs.c;
+ };
+ module = {
+   name = sfs;
+   common = fs/sfs.c;
+ };
+ module = {
+   name = tar;
+   common = fs/tar.c;
+ };
+ module = {
+   name = udf;
+   common = fs/udf.c;
+ };
+ module = {
+   name = ufs1;
+   common = fs/ufs.c;
+ };
+ module = {
+   name = ufs2;
+   common = fs/ufs2.c;
+ };
+ module = {
+   name = xfs;
+   common = fs/xfs.c;
+ };
+ module = {
+   name = pxe;
+   i386_pc = fs/i386/pc/pxe.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = gettext;
+   common = gettext/gettext.c;
+ };
+ module = {
+   name = gfxmenu;
+   common = gfxmenu/gfxmenu.c;
+   common = gfxmenu/model.c;
+   common = gfxmenu/view.c;
+   common = gfxmenu/font.c;
+   common = gfxmenu/icon_manager.c;
+   common = gfxmenu/theme_loader.c;
+   common = gfxmenu/widget-box.c;
+   common = gfxmenu/gui_canvas.c;
+   common = gfxmenu/gui_circular_progress.c;
+   common = gfxmenu/gui_box.c;
+   common = gfxmenu/gui_label.c;
+   common = gfxmenu/gui_list.c;
+   common = gfxmenu/gui_image.c;
+   common = gfxmenu/gui_progress_bar.c;
+   common = gfxmenu/gui_util.c;
+   common = gfxmenu/gui_string_util.c;
+   common = gfxmenu/named_colors.c;
+ };
+ module = {
+   name = hello;
+   common = hello/hello.c;
+ };
+ module = {
+   name = gzio;
+   common = io/gzio.c;
+ };
+ module = {
+   name = bufio;
+   common = io/bufio.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = elf;
+   common = kern/elf.c;
+ };
+ module = {
+   name = crypto;
+   common = lib/crypto.c;
+   extra_dist = lib/libgcrypt-grub/cipher/crypto.lst;
+ };
+ module = {
+   name = pbkdf2;
+   common = lib/pbkdf2.c;
+ };
+ module = {
+   name = relocator;
+   common = lib/relocator.c;
+   x86 = lib/i386/relocator16.S;
+   x86 = lib/i386/relocator32.S;
+   x86 = lib/i386/relocator64.S;
+   i386 = lib/i386/relocator_asm.S;
+   x86_64 = lib/x86_64/relocator_asm.S;
+   x86 = lib/i386/relocator.c;
+   ieee1275 = lib/ieee1275/relocator.c;
+   x86_efi = lib/efi/relocator.c;
+   mips = lib/mips/relocator_asm.S;
+   mips = lib/mips/relocator.c;
+   powerpc = lib/powerpc/relocator_asm.S;
+   powerpc = lib/powerpc/relocator.c;
+   enable = mips;
+   enable = powerpc;
+   enable = x86;
+ };
+ module = {
+   name = datetime;
+   x86_noefi_mips = lib/cmos_datetime.c;
+   x86_efi = lib/efi/datetime.c;
+   sparc64_ieee1275 = lib/ieee1275/datetime.c;
+   powerpc_ieee1275 = lib/ieee1275/datetime.c;
+   enable = x86;
+   enable = mips;
+   enable = sparc64_ieee1275;
+   enable = powerpc_ieee1275;
+ };
+ module = {
+   name = setjmp;
+   common = lib/setjmp.S;
+   extra_dist = lib/i386/setjmp.S;
+   extra_dist = lib/mips/setjmp.S;
+   extra_dist = lib/x86_64/setjmp.S;
+   extra_dist = lib/sparc64/setjmp.S;
+   extra_dist = lib/powerpc/setjmp.S;
+ };
+ module = {
+   name = aout;
+   common = loader/aout.c;
+   enable = x86;
+ };
+ module = {
+   name = bsd;
+   x86 = loader/i386/bsd.c;
+   x86 = loader/i386/bsd32.c;
+   x86 = loader/i386/bsd64.c;
+   extra_dist = loader/i386/bsdXX.c;
+   extra_dist = loader/i386/bsd_pagetable.c;
+   enable = x86;
+ };
+ module = {
+   name = linux16;
+   i386_pc = loader/i386/pc/linux.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = ntldr;
+   i386_pc = loader/i386/pc/ntldr.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = multiboot2;
+   cppflags = "-DGRUB_USE_MULTIBOOT2";
+   common = loader/multiboot.c;
+   common = loader/multiboot_mbi2.c;
+   enable = x86;
+   enable = mips;
+ };
+ module = {
+   name = multiboot;
+   common = loader/multiboot.c;
+   x86 = loader/i386/multiboot_mbi.c;
+   extra_dist = loader/multiboot_elfxx.c;
+   enable = x86;
+ };
+ module = {
+   name = linux;
+   x86 = loader/i386/linux.c;
+   mips = loader/mips/linux.c;
+   powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c;
+   sparc64_ieee1275 = loader/sparc64/ieee1275/linux.c;
+   enable = noemu;
+ };
+ module = {
+   name = xnu;
+   x86 = loader/xnu_resume.c;
+   x86 = loader/i386/xnu.c;
+   x86 = loader/macho32.c;
+   x86 = loader/macho64.c;
+   x86 = loader/macho.c;
+   x86 = loader/xnu.c;
+   extra_dist = loader/machoXX.c;
+   enable = x86;
+ };
+ module = {
+   name = appleldr;
+   x86_efi = loader/efi/appleloader.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = chain;
+   x86_efi = loader/efi/chainloader.c;
+   i386_pc = loader/i386/pc/chainloader.c;
+   enable = i386_pc;
+   enable = x86_efi;
+ };
+ module = {
+   name = mmap;
+   i386_pc = mmap/mmap.c;
+   i386_pc = mmap/i386/uppermem.c;
+   i386_pc = mmap/i386/mmap.c;
+   i386_pc = mmap/i386/pc/mmap.c;
+   i386_pc = mmap/i386/pc/mmap_helper.S;
+   x86_efi = mmap/mmap.c;
+   x86_efi = mmap/i386/uppermem.c;
+   x86_efi = mmap/i386/mmap.c;
+   x86_efi = mmap/efi/mmap.c;
+   i386_coreboot = mmap/mmap.c;
+   i386_coreboot = mmap/i386/uppermem.c;
+   i386_coreboot = mmap/i386/mmap.c;
+   i386_multiboot = mmap/mmap.c;
+   i386_multiboot = mmap/i386/uppermem.c;
+   i386_multiboot = mmap/i386/mmap.c;
+   i386_qemu = mmap/mmap.c;
+   i386_qemu = mmap/i386/uppermem.c;
+   i386_qemu = mmap/i386/mmap.c;
+   i386_ieee1275 = mmap/mmap.c;
+   i386_ieee1275 = mmap/i386/uppermem.c;
+   i386_ieee1275 = mmap/i386/mmap.c;
+   mips_yeeloong = mmap/mmap.c;
+   mips_yeeloong = mmap/mips/yeeloong/uppermem.c;
+   enable = x86;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = normal;
+   common = normal/main.c;
+   common = normal/cmdline.c;
+   common = normal/dyncmd.c;
+   common = normal/auth.c;
+   common = normal/autofs.c;
+   common = normal/color.c;
+   common = normal/completion.c;
+   common = normal/datetime.c;
+   common = normal/menu.c;
+   common = normal/menu_entry.c;
+   common = normal/menu_text.c;
+   common = normal/misc.c;
+   common = normal/crypto.c;
+   common = normal/term.c;
+   common = normal/context.c;
+   common = normal/charset.c;
+   common = script/main.c;
+   common = script/script.c;
+   common = script/execute.c;
+   common = script/function.c;
+   common = script/lexer.c;
+   common = script/argv.c;
+   common = unidata.c;
+   common_nodist = grub_script.tab.c;
+   common_nodist = grub_script.yy.c;
+   common_nodist = grub_script.tab.h;
+   common_nodist = grub_script.yy.h;
+   extra_dist = script/yylex.l;
+   extra_dist = script/parser.y;
+   cflags = '$(CFLAGS_POSIX) -Wno-error';
+   cppflags = '$(CPPFLAGS_POSIX)';
+ };
+ module = {
+   name = part_acorn;
+   common = partmap/acorn.c;
+ };
+ module = {
+   name = part_amiga;
+   common = partmap/amiga.c;
+ };
+ module = {
+   name = part_apple;
+   common = partmap/apple.c;
+ };
+ module = {
+   name = part_gpt;
+   common = partmap/gpt.c;
+ };
+ module = {
+   name = part_msdos;
+   common = partmap/msdos.c;
+ };
+ module = {
+   name = part_sun;
+   common = partmap/sun.c;
+ };
+ module = {
+   name = part_bsd;
+   common = partmap/bsdlabel.c;
+ };
+ module = {
+   name = part_sunpc;
+   common = partmap/sunpc.c;
+ };
+ module = {
+   name = msdospart;
+   common = parttool/msdospart.c;
+ };
+ module = {
+   name = at_keyboard;
+   common = term/at_keyboard.c;
+   enable = x86;
+ };
+ module = {
+   name = gfxterm;
+   common = term/gfxterm.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = serial;
+   common = term/serial.c;
+   x86 = term/ns8250.c;
+   enable = emu;
+   enable = i386;
+   enable = x86_64_efi;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = sendkey;
+   i386_pc = commands/i386/pc/sendkey.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = terminfo;
+   common = term/terminfo.c;
+   common = term/tparm.c;
+ };
+ module = {
+   name = usb_keyboard;
+   common = term/usb_keyboard.c;
+   enable = i386_pc;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = vga;
+   i386_pc = video/i386/pc/vga.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = vga_text;
+   common = term/i386/pc/vga_text.c;
+   common = term/i386/vga_common.c;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+ };
+ module = {
+   name = video_cirrus;
+   x86 = video/cirrus.c;
+   enable = x86;
+ };
+ module = {
+   name = video_bochs;
+   x86 = video/bochs.c;
+   enable = x86;
+ };
+ module = {
+   name = functional_test;
+   common = tests/lib/functional_test.c;
+   common = tests/lib/test.c;
+ };
+ module = {
+   name = example_functional_test;
+   common = tests/example_functional_test.c;
+   cflags = -Wno-format;
+ };
+ module = {
+   name = bitmap;
+   common = video/bitmap.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = bitmap_scale;
+   common = video/bitmap_scale.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = efi_gop;
+   x86_efi = video/efi_gop.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = efi_uga;
+   x86_efi = video/efi_uga.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = jpeg;
+   common = video/readers/jpeg.c;
+ };
+ module = {
+   name = png;
+   common = video/readers/png.c;
+ };
+ module = {
+   name = tga;
+   common = video/readers/tga.c;
+ };
+ module = {
+   name = vbe;
+   i386_pc = video/i386/pc/vbe.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = video_fb;
+   common = video/fb/video_fb.c;
+   common = video/fb/fbblit.c;
+   common = video/fb/fbfill.c;
+   common = video/fb/fbutil.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = video;
+   common = video/video.c;
+   enable = emu;
+   enable = x86;
+   enable = sparc64;
+   enable = powerpc;
+ };
+ module = {
+   name = ieee1275_fb;
+   ieee1275 = video/ieee1275.c;
+   enable = powerpc;
+   enable = sparc64;
+ };
+ module = {
+   name = sdl;
+   emu = video/emu/sdl.c;
+   enable = emu;
+   condition = COND_GRUB_EMU_SDL;
+ };
+ module = {
+   name = datehook;
+   common = hook/datehook.c;
+ };
++
++module = {
++  name = keylayouts;
++  common = commands/keylayouts.c;
++  enable = emu;
++  enable = x86;
++  enable = sparc64;
++  enable = powerpc;
++};
index 0000000000000000000000000000000000000000,7f757485c4a3781cd69ed4a9e18053d8c2e94276..e3cd490ac518a6ce0f40464852f313fbfeb95f92
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1419 +1,1499 @@@
 -grub_ohci_transfer (grub_usb_controller_t dev,
 -                  grub_usb_transfer_t transfer, int timeout,
 -                  grub_size_t *actual)
+ /* ohci.c - OHCI Support.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/mm.h>
+ #include <grub/usb.h>
+ #include <grub/usbtrans.h>
+ #include <grub/misc.h>
+ #include <grub/pci.h>
+ #include <grub/cpu/pci.h>
+ #include <grub/cpu/io.h>
+ #include <grub/time.h>
+ #include <grub/cs5536.h>
+ #include <grub/loader.h>
+ struct grub_ohci_hcca
+ {
+   /* Pointers to Interrupt Endpoint Descriptors.  Not used by
+      GRUB.  */
+   grub_uint32_t inttable[32];
+   /* Current frame number.  */
+   grub_uint16_t framenumber;
+   grub_uint16_t pad;
+   /* List of completed TDs.  */
+   grub_uint32_t donehead;
+   grub_uint8_t reserved[116];
+ } __attribute__((packed));
+ /* OHCI General Transfer Descriptor */
+ struct grub_ohci_td
+ {
+   /* Information used to construct the TOKEN packet.  */
+   grub_uint32_t token;
+   grub_uint32_t buffer; /* LittleEndian physical address */
+   grub_uint32_t next_td; /* LittleEndian physical address */
+   grub_uint32_t buffer_end; /* LittleEndian physical address */
+   /* next values are not for OHCI HW */
+   grub_uint32_t prev_td_phys; /* we need it to find previous TD
+                                * physical address in CPU endian */
+   grub_uint32_t link_td; /* pointer to next free/chained TD
+                           * pointer as uint32 */
+   grub_uint32_t tr_index; /* index of TD in transfer */
+   grub_uint8_t pad[4]; /* padding to 32 bytes */
+ } __attribute__((packed));
+ /* OHCI Endpoint Descriptor.  */
+ struct grub_ohci_ed
+ {
+   grub_uint32_t target;
+   grub_uint32_t td_tail;
+   grub_uint32_t td_head;
+   grub_uint32_t next_ed;
+ } __attribute__((packed));
+ typedef volatile struct grub_ohci_td *grub_ohci_td_t;
+ typedef volatile struct grub_ohci_ed *grub_ohci_ed_t;
+ /* Experimental change of ED/TD allocation */
+ /* Little bit similar as in UHCI */
+ /* Implementation assumes:
+  *      32-bits architecture - XXX: fix for 64-bits
+  *      memory allocated by grub_memalign_dma32 must be continuous
+  *      in virtual and also in physical memory */
+ struct grub_ohci
+ {
+   volatile grub_uint32_t *iobase;
+   volatile struct grub_ohci_hcca *hcca;
+   grub_uint32_t hcca_addr;
+   struct grub_pci_dma_chunk *hcca_chunk;
+   grub_ohci_ed_t ed_ctrl; /* EDs for CONTROL */
+   grub_uint32_t ed_ctrl_addr;
+   struct grub_pci_dma_chunk *ed_ctrl_chunk;
+   grub_ohci_ed_t ed_bulk; /* EDs for BULK */
+   grub_uint32_t ed_bulk_addr;
+   struct grub_pci_dma_chunk *ed_bulk_chunk;
+   grub_ohci_td_t td; /* TDs */
+   grub_uint32_t td_addr;
+   struct grub_pci_dma_chunk *td_chunk;
+   struct grub_ohci *next;
+   grub_ohci_td_t td_free; /* Pointer to first free TD */
+   int bad_OHCI;
+ };
+ static struct grub_ohci *ohci;
+ typedef enum
+ {
+   GRUB_OHCI_REG_REVISION = 0x00,
+   GRUB_OHCI_REG_CONTROL,
+   GRUB_OHCI_REG_CMDSTATUS,
+   GRUB_OHCI_REG_INTSTATUS,
+   GRUB_OHCI_REG_INTENA,
+   GRUB_OHCI_REG_INTDIS,
+   GRUB_OHCI_REG_HCCA,
+   GRUB_OHCI_REG_PERIODIC,
+   GRUB_OHCI_REG_CONTROLHEAD,
+   GRUB_OHCI_REG_CONTROLCURR,
+   GRUB_OHCI_REG_BULKHEAD,
+   GRUB_OHCI_REG_BULKCURR,
+   GRUB_OHCI_REG_DONEHEAD,
+   GRUB_OHCI_REG_FRAME_INTERVAL,
+   GRUB_OHCI_REG_PERIODIC_START = 16,
+   GRUB_OHCI_REG_RHUBA = 18,
+   GRUB_OHCI_REG_RHUBPORT = 21,
+   GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
+   GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
+   GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
+   GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
+ } grub_ohci_reg_t;
+ #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
+ #define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200
+ #define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_MASK 0x8fff0000
+ #define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT 16
+ #define GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT 0
+ /* XXX: Is this choice of timings sane?  */
+ #define GRUB_OHCI_FSMPS 0x2778
+ #define GRUB_OHCI_PERIODIC_START 0x257f
+ #define GRUB_OHCI_FRAME_INTERVAL 0x2edf
+ #define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
+ #define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
+ #define GRUB_OHCI_SET_PORT_RESET (1 << 4)
+ #define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)
+ #define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5)
+ #define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4)
+ #define GRUB_OHCI_RESET_CONNECT_CHANGE (1 << 16)
+ #define GRUB_OHCI_CTRL_EDS 16
+ #define GRUB_OHCI_BULK_EDS 16
+ #define GRUB_OHCI_TDS 256
+ #define GRUB_OHCI_ED_ADDR_MASK 0x7ff
+ static inline grub_ohci_ed_t
+ grub_ohci_ed_phys2virt (struct grub_ohci *o, int bulk, grub_uint32_t x)
+ {
+   if (!x)
+     return NULL;
+   if (bulk)
+     return (grub_ohci_ed_t) (x - o->ed_bulk_addr
+                            + (grub_uint8_t *) o->ed_bulk);
+   return (grub_ohci_ed_t) (x - o->ed_ctrl_addr
+                          + (grub_uint8_t *) o->ed_ctrl);
+ }
+ static grub_uint32_t
+ grub_ohci_virt_to_phys (struct grub_ohci *o, int bulk, grub_ohci_ed_t x)
+ {
+   if (!x)
+     return 0;
+   if (bulk)
+     return (grub_uint8_t *) x - (grub_uint8_t *) o->ed_bulk + o->ed_bulk_addr;
+   return (grub_uint8_t *) x - (grub_uint8_t *) o->ed_ctrl + o->ed_ctrl_addr;
+ }
+ static inline grub_ohci_td_t
+ grub_ohci_td_phys2virt (struct grub_ohci *o, grub_uint32_t x)
+ {
+   if (!x)
+     return NULL;
+   return (grub_ohci_td_t) (x - o->td_addr + (grub_uint8_t *) o->td);
+ }
+ static grub_uint32_t
+ grub_ohci_td_virt2phys (struct grub_ohci *o,  grub_ohci_td_t x)
+ {
+   if (!x)
+     return 0;
+   return (grub_uint8_t *)x - (grub_uint8_t *)o->td + o->td_addr;
+ }
+   
+ static grub_uint32_t
+ grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
+ {
+   return grub_le_to_cpu32 (*(o->iobase + reg));
+ }
+ static void
+ grub_ohci_writereg32 (struct grub_ohci *o,
+                     grub_ohci_reg_t reg, grub_uint32_t val)
+ {
+   *(o->iobase + reg) = grub_cpu_to_le32 (val);
+ }
\f
+ /* Iterate over all PCI devices.  Determine if a device is an OHCI
+    controller.  If this is the case, initialize it.  */
+ static int NESTED_FUNC_ATTR
+ grub_ohci_pci_iter (grub_pci_device_t dev,
+                   grub_pci_id_t pciid)
+ {
+   grub_uint32_t interf;
+   grub_uint32_t base;
+   grub_pci_address_t addr;
+   struct grub_ohci *o;
+   grub_uint32_t revision;
+   int cs5536;
+   int j;
+   
+   /* Determine IO base address.  */
+   grub_dprintf ("ohci", "pciid = %x\n", pciid);
+   if (pciid == GRUB_CS5536_PCIID)
+     {
+       grub_uint64_t basereg;
+       cs5536 = 1;
+       basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE);
+       if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE))
+       {
+         /* Shouldn't happen.  */
+         grub_dprintf ("ohci", "No OHCI address is assigned\n");
+         return 0;
+       }
+       base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK);
+       basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER;
+       basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED;
+       basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS;
+       grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE, basereg);
+     }
+   else
+     {
+       grub_uint32_t class_code;
+       grub_uint32_t class;
+       grub_uint32_t subclass;
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+       class_code = grub_pci_read (addr) >> 8;
+       
+       interf = class_code & 0xFF;
+       subclass = (class_code >> 8) & 0xFF;
+       class = class_code >> 16;
+       /* If this is not an OHCI controller, just return.  */
+       if (class != 0x0c || subclass != 0x03 || interf != 0x10)
+       return 0;
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+       base = grub_pci_read (addr);
+ #if 0
+       /* Stop if there is no IO space base address defined.  */
+       if (! (base & 1))
+       return 0;
+ #endif
+       grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x\n",
+                   class, subclass, interf);
+     }
+   /* Allocate memory for the controller and register it.  */
+   o = grub_malloc (sizeof (*o));
+   if (! o)
+     return 1;
+   grub_memset ((void*)o, 0, sizeof (*o));
+   o->iobase = grub_pci_device_map_range (dev, base, 0x800);
+   grub_dprintf ("ohci", "base=%p\n", o->iobase);
+   /* Reserve memory for the HCCA.  */
+   o->hcca_chunk = grub_memalign_dma32 (256, 256);
+   if (! o->hcca_chunk)
+     goto fail;
+   o->hcca = grub_dma_get_virt (o->hcca_chunk);
+   o->hcca_addr = grub_dma_get_phys (o->hcca_chunk);
+   grub_memset ((void*)o->hcca, 0, sizeof(*o->hcca));
+   grub_dprintf ("ohci", "hcca: chunk=%p, virt=%p, phys=0x%02x\n",
+                 o->hcca_chunk, o->hcca, o->hcca_addr);
+   /* Reserve memory for ctrl EDs.  */
+   o->ed_ctrl_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_CTRL_EDS);
+   if (! o->ed_ctrl_chunk)
+     goto fail;
+   o->ed_ctrl = grub_dma_get_virt (o->ed_ctrl_chunk);
+   o->ed_ctrl_addr = grub_dma_get_phys (o->ed_ctrl_chunk);
+   /* Preset EDs */
+   grub_memset ((void*)o->ed_ctrl, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_CTRL_EDS);
+   for (j=0; j < GRUB_OHCI_CTRL_EDS; j++)
+     o->ed_ctrl[j].target = grub_cpu_to_le32 (1 << 14); /* skip */
+     
+   grub_dprintf ("ohci", "EDs-C: chunk=%p, virt=%p, phys=0x%02x\n",
+                 o->ed_ctrl_chunk, o->ed_ctrl, o->ed_ctrl_addr);
+   /* Reserve memory for bulk EDs.  */
+   o->ed_bulk_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_BULK_EDS);
+   if (! o->ed_bulk_chunk)
+     goto fail;
+   o->ed_bulk = grub_dma_get_virt (o->ed_bulk_chunk);
+   o->ed_bulk_addr = grub_dma_get_phys (o->ed_bulk_chunk);
+   /* Preset EDs */
+   grub_memset ((void*)o->ed_bulk, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_BULK_EDS);
+   for (j=0; j < GRUB_OHCI_BULK_EDS; j++)
+     o->ed_bulk[j].target = grub_cpu_to_le32 (1 << 14); /* skip */
+   grub_dprintf ("ohci", "EDs-B: chunk=%p, virt=%p, phys=0x%02x\n",
+                 o->ed_bulk_chunk, o->ed_bulk, o->ed_bulk_addr);
+   /* Reserve memory for TDs.  */
+   o->td_chunk = grub_memalign_dma32 (32, sizeof(struct grub_ohci_td)*GRUB_OHCI_TDS);
+   /* Why is it aligned on 32 boundary if spec. says 16 ?
+    * We have structure 32 bytes long and we don't want cross
+    * 4K boundary inside structure. */
+   if (! o->td_chunk)
+     goto fail;
+   o->td_free = o->td = grub_dma_get_virt (o->td_chunk);
+   o->td_addr = grub_dma_get_phys (o->td_chunk);
+   /* Preset free TDs chain in TDs */
+   grub_memset ((void*)o->td, 0, sizeof(struct grub_ohci_td) * GRUB_OHCI_TDS);
+   for (j=0; j < (GRUB_OHCI_TDS-1); j++)
+     o->td[j].link_td = (grub_uint32_t)&o->td[j+1];
+   grub_dprintf ("ohci", "TDs: chunk=%p, virt=%p, phys=0x%02x\n",
+                 o->td_chunk, o->td, o->td_addr);
+   /* Check if the OHCI revision is actually 1.0 as supported.  */
+   revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION);
+   grub_dprintf ("ohci", "OHCI revision=0x%02x\n", revision & 0xFF);
+   if ((revision & 0xFF) != 0x10)
+     goto fail;
+   {
+     grub_uint32_t control;
+     /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
+     control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+     if ((control & 0x100) != 0)
+       {
+       unsigned i;
+       grub_dprintf("ohci", "OHCI is owned by SMM\n");
+       /* Do change of ownership */
+       /* Ownership change request */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
+       /* Waiting for SMM deactivation */
+       for (i=0; i < 10; i++)
+         {
+           if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
+             {
+               grub_dprintf("ohci", "Ownership changed normally.\n");
+               break;
+             }
+           grub_millisleep (100);
+           }
+       if (i >= 10)
+         {
+           grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
+           grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
+         }
+       }
+     else if (((control & 0x100) == 0) && 
+            ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
+       {
+       grub_dprintf("ohci", "OHCI is owned by BIOS\n");
+       /* Do change of ownership - not implemented yet... */
+       /* In fact we probably need to do nothing ...? */
+       }
+     else
+       {
+       grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
+       /* We can setup OHCI. */
+       }  
+   }
+   /* Suspend the OHCI by issuing a reset.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
+   grub_millisleep (1);
+   grub_dprintf ("ohci", "OHCI reset\n");
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL,
+                       (GRUB_OHCI_FSMPS
+                        << GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT)
+                       | (GRUB_OHCI_FRAME_INTERVAL
+                          << GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT));
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START,
+                       GRUB_OHCI_PERIODIC_START);
+   /* Setup the HCCA.  */
+   o->hcca->donehead = 0;
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
+   grub_dprintf ("ohci", "OHCI HCCA\n");
+   /* Misc. pre-sets. */
+   o->hcca->donehead = 0;
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+   /* We don't want modify CONTROL/BULK HEAD registers.
+    * So we assign to HEAD registers zero ED from related array
+    * and we will not use this ED, it will be always skipped.
+    * It should not produce notable performance penalty (I hope). */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+   /* Check OHCI Legacy Support */
+   if ((revision & 0x100) != 0)
+     {
+       grub_dprintf ("ohci", "Legacy Support registers detected\n");
+       grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
+                   grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
+                           (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
+       grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
+     }
+   /* Enable the OHCI + enable CONTROL and BULK LIST.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                       (2 << 6)
+                       | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
+                       | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
+   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
+               (grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
+   /* Power on all ports */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                        (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                         & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                        | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+   /* Now we have hot-plugging, we need to wait for stable power only */
+   grub_millisleep (100);
+   /* Link to ohci now that initialisation is successful.  */
+   o->next = ohci;
+   ohci = o;
+   return 0;
+  fail:
+   if (o)
+     grub_dma_free (o->td_chunk);
+     grub_dma_free (o->ed_bulk_chunk);
+     grub_dma_free (o->ed_ctrl_chunk);
+     grub_dma_free (o->hcca_chunk);
+   grub_free (o);
+   return 0;
+ }
+ static void
+ grub_ohci_inithw (void)
+ {
+   grub_pci_iterate (grub_ohci_pci_iter);
+ }
\f
+ static int
+ grub_ohci_iterate (int (*hook) (grub_usb_controller_t dev))
+ {
+   struct grub_ohci *o;
+   struct grub_usb_controller dev;
+   for (o = ohci; o; o = o->next)
+     {
+       dev.data = o;
+       if (hook (&dev))
+       return 1;
+     }
+   return 0;
+ }
+ static grub_ohci_ed_t
+ grub_ohci_find_ed (struct grub_ohci *o, int bulk, grub_uint32_t target)
+ {
+   grub_ohci_ed_t ed, ed_next;
+   grub_uint32_t target_addr = target & GRUB_OHCI_ED_ADDR_MASK;
+   int count;
+   int i;
+   /* Use proper values and structures. */
+   if (bulk)
+     {    
+       count = GRUB_OHCI_BULK_EDS;
+       ed = o->ed_bulk;
+       ed_next = grub_ohci_ed_phys2virt(o, bulk,
+                   grub_le_to_cpu32 (ed->next_ed) );
+     }
+   else
+     {
+       count = GRUB_OHCI_CTRL_EDS;
+       ed = o->ed_ctrl;
+       ed_next = grub_ohci_ed_phys2virt(o, bulk,
+                   grub_le_to_cpu32 (ed->next_ed) );
+     }
+    /* First try to find existing ED with proper target address */
+   for (i = 0; ; )
+     {
+       if (i && /* We ignore zero ED */
+            ((ed->target & GRUB_OHCI_ED_ADDR_MASK) == target_addr))
+         return ed; /* Found proper existing ED */
+       i++;
+       if (ed_next && (i < count))
+         {
+           ed = ed_next;
+           ed_next = grub_ohci_ed_phys2virt(o, bulk,
+                       grub_le_to_cpu32 (ed->next_ed) );
+           continue;
+         }
+       break;
+     }
+   /* ED with target_addr does not exist, we have to add it */
+   /* Have we any free ED in array ? */
+   if (i >= count) /* No. */
+     return NULL;
+   /* Currently we simply take next ED in array, no allocation
+    * function is used. It should be no problem until hot-plugging
+    * will be implemented, i.e. until we will need to de-allocate EDs
+    * of unplugged devices. */
+   /* We can link new ED to previous ED safely as the new ED should
+    * still have set skip bit. */
+   ed->next_ed = grub_cpu_to_le32 ( grub_ohci_virt_to_phys (o,
+                                      bulk, &ed[1]));
+   return &ed[1];
+ }
+ static grub_ohci_td_t
+ grub_ohci_alloc_td (struct grub_ohci *o)
+ {
+   grub_ohci_td_t ret;
+   /* Check if there is a Transfer Descriptor available.  */
+   if (! o->td_free)
+     return NULL;
+   ret = o->td_free; /* Take current free TD */
+   o->td_free = (grub_ohci_td_t)ret->link_td; /* Advance to next free TD in chain */
+   ret->link_td = 0; /* Reset link_td in allocated TD */
+   return ret;
+ }
+ static void
+ grub_ohci_free_td (struct grub_ohci *o, grub_ohci_td_t td)
+ {
+   grub_memset ( (void*)td, 0, sizeof(struct grub_ohci_td) ); 
+   td->link_td = (grub_uint32_t) o->td_free; /* Cahin new free TD & rest */
+   o->td_free = td; /* Change address of first free TD */
+ }
+ static void
+ grub_ohci_free_tds (struct grub_ohci *o, grub_ohci_td_t td)
+ {
+   if (!td)
+     return;
+     
+   /* Unchain first TD from previous TD if it is chained */
+   if (td->prev_td_phys)
+     {
+       grub_ohci_td_t td_prev_virt = grub_ohci_td_phys2virt(o,
+                                       td->prev_td_phys);
+       if (td == (grub_ohci_td_t) td_prev_virt->link_td)
+         td_prev_virt->link_td = 0;
+     }
+   
+   /* Free all TDs from td  (chained by link_td) */
+   while (td)
+     {
+       grub_ohci_td_t tdprev;
+       
+       /* Unlink the queue.  */
+       tdprev = td;
+       td = (grub_ohci_td_t) td->link_td;
+       /* Free the TD.  */
+       grub_ohci_free_td (o, tdprev);
+     }
+ }
+ static void
+ grub_ohci_transaction (grub_ohci_td_t td,
+                      grub_transfer_type_t type, unsigned int toggle,
+                      grub_size_t size, grub_uint32_t data)
+ {
+   grub_uint32_t token;
+   grub_uint32_t buffer;
+   grub_uint32_t buffer_end;
+   grub_dprintf ("ohci", "OHCI transaction td=%p type=%d, toggle=%d, size=%d\n",
+               td, type, toggle, size);
+   switch (type)
+     {
+     case GRUB_USB_TRANSFER_TYPE_SETUP:
+       token = 0 << 19;
+       break;
+     case GRUB_USB_TRANSFER_TYPE_IN:
+       token = 2 << 19;
+       break;
+     case GRUB_USB_TRANSFER_TYPE_OUT:
+       token = 1 << 19;
+       break;
+     default:
+       token = 0;
+       break;
+     }
+   /* Set the token (Always generate interrupt - bits 21-23 = 0).  */
+   token |= toggle << 24;
+   token |= 1 << 25;
+   /* Set "Not accessed" error code */
+   token |= 15 << 28;
+   buffer = data;
+   buffer_end = buffer + size - 1;
+   /* Set correct buffer values in TD if zero transfer occurs */
+   if (size)
+     {
+       buffer = (grub_uint32_t) data;
+       buffer_end = buffer + size - 1;
+       td->buffer = grub_cpu_to_le32 (buffer);
+       td->buffer_end = grub_cpu_to_le32 (buffer_end);
+     }
+   else 
+     {
+       td->buffer = 0;
+       td->buffer_end = 0;
+     }
+   /* Set the rest of TD */
+   td->token = grub_cpu_to_le32 (token);
+   td->next_td = 0;
+ }
++struct grub_ohci_transfer_controller_data
++{
++  grub_uint32_t tderr_phys;
++  grub_uint32_t td_last_phys;
++  grub_ohci_ed_t ed_virt;
++  grub_ohci_td_t td_current_virt;
++  grub_ohci_td_t td_head_virt;
++  grub_uint64_t bad_OHCI_delay;
++};
++
+ static grub_usb_err_t
 -  grub_ohci_ed_t ed_virt;
++grub_ohci_setup_transfer (grub_usb_controller_t dev,
++                        grub_usb_transfer_t transfer)
+ {
+   struct grub_ohci *o = (struct grub_ohci *) dev->data;
 -  grub_ohci_td_t td_head_virt;
 -  grub_ohci_td_t td_current_virt;
+   int bulk = 0;
 -  grub_ohci_td_t tderr_virt = NULL;
+   grub_ohci_td_t td_next_virt;
 -  grub_uint32_t td_last_phys;
 -  grub_uint32_t tderr_phys = 0;
 -  grub_uint32_t status;
 -  grub_uint32_t control;
 -  grub_uint8_t errcode = 0;
 -  grub_usb_err_t err = GRUB_USB_ERR_NONE;
+   grub_uint32_t target;
+   grub_uint32_t td_head_phys;
+   grub_uint32_t td_tail_phys;
 -  grub_uint64_t maxtime;
 -  grub_uint64_t bad_OHCI_delay = 0;
 -  int err_halt = 0;
 -  int err_timeout = 0;
 -  int err_unrec = 0;
 -  grub_uint32_t intstatus;
+   int i;
 -  *actual = 0;
++  struct grub_ohci_transfer_controller_data *cdata;
 -      default :
++  cdata = grub_zalloc (sizeof (*cdata));
++  if (!cdata)
++    return GRUB_USB_ERR_INTERNAL;
+   /* Pre-set target for ED - we need it to find proper ED */
+   /* Set the device address.  */
+   target = transfer->devaddr;
+   /* Set the endpoint. It should be masked, we need 4 bits only. */
+   target |= (transfer->endpoint & 15) << 7;
+   /* Set the device speed.  */
+   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
+   /* Set the maximum packet size.  */
+   target |= transfer->max << 16;
+   /* Determine if transfer type is bulk - we need to select proper ED */
+   switch (transfer->type)
+     {
+       case GRUB_USB_TRANSACTION_TYPE_BULK:
+         bulk = 1;
+       break;
+       case GRUB_USB_TRANSACTION_TYPE_CONTROL:
+         break;
 -  ed_virt = grub_ohci_find_ed (o, bulk, target);
 -  if (!ed_virt)
++      default:
++      grub_free (cdata);
+         return GRUB_USB_ERR_INTERNAL;
+     }
+   /* Find proper ED or add new ED */
 -  td_head_phys = grub_le_to_cpu32 (ed_virt->td_head) & ~0xf;
 -  td_tail_phys = grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf;
++  cdata->ed_virt = grub_ohci_find_ed (o, bulk, target);
++  if (!cdata->ed_virt)
+     {
+       grub_dprintf ("ohci","Fatal: No free ED !\n");
++      grub_free (cdata);
+       return GRUB_USB_ERR_INTERNAL;
+     }
+   
+   /* Take pointer to first TD from ED */
 -      td_head_virt = grub_ohci_alloc_td (o);
 -      if (!td_head_virt)
++  td_head_phys = grub_le_to_cpu32 (cdata->ed_virt->td_head) & ~0xf;
++  td_tail_phys = grub_le_to_cpu32 (cdata->ed_virt->td_tail) & ~0xf;
+   /* Sanity check - td_head should be equal to td_tail */
+   if (td_head_phys != td_tail_phys) /* Should never happen ! */
+     {
+       grub_dprintf ("ohci", "Fatal: HEAD is not equal to TAIL !\n");
+       grub_dprintf ("ohci", "HEAD = 0x%02x, TAIL = 0x%02x\n",
+                     td_head_phys, td_tail_phys);
+       /* XXX: Fix: What to do ? */
++      grub_free (cdata);
+       return GRUB_USB_ERR_INTERNAL;
+     }
+   
+   /* Now we should handle first TD. If ED is newly allocated,
+    * we must allocate the first TD. */
+   if (!td_head_phys)
+     {
 -      ed_virt->td_head = grub_cpu_to_le32 ( grub_ohci_td_virt2phys (o,
 -                                              td_head_virt) );
 -      ed_virt->td_tail = ed_virt->td_head;
++      cdata->td_head_virt = grub_ohci_alloc_td (o);
++      if (!cdata->td_head_virt)
+         return GRUB_USB_ERR_INTERNAL; /* We don't need de-allocate ED */
+       /* We can set td_head only when ED is not active, i.e.
+        * when it is newly allocated. */
 -    td_head_virt = grub_ohci_td_phys2virt ( o, td_head_phys );
++      cdata->ed_virt->td_head
++      = grub_cpu_to_le32 (grub_ohci_td_virt2phys (o, cdata->td_head_virt));
++      cdata->ed_virt->td_tail = cdata->ed_virt->td_head;
+     }
+   else
 -  td_last_phys = td_head_phys; /* initial value to make compiler happy... */
 -  for (i = 0, td_current_virt = td_head_virt;
++    cdata->td_head_virt = grub_ohci_td_phys2virt ( o, td_head_phys );
+     
+   /* Set TDs */
 -      grub_ohci_transaction (td_current_virt, tr->pid, tr->toggle,
++  cdata->td_last_phys = td_head_phys; /* initial value to make compiler happy... */
++  for (i = 0, cdata->td_current_virt = cdata->td_head_virt;
+        i < transfer->transcnt; i++)
+     {
+       grub_usb_transaction_t tr = &transfer->transactions[i];
 -      td_current_virt->tr_index = (grub_uint32_t) i;
++      grub_ohci_transaction (cdata->td_current_virt, tr->pid, tr->toggle,
+                            tr->size, tr->data);
+       /* Set index of TD in transfer */
 -        td_current_virt->token |= grub_cpu_to_le32 ( 7 << 21);
++      cdata->td_current_virt->tr_index = (grub_uint32_t) i;
+       /* No IRQ request in TD if bad_OHCI set */
+       if (o->bad_OHCI)
 -      td_last_phys = grub_ohci_td_virt2phys (o, td_current_virt);
++        cdata->td_current_virt->token |= grub_cpu_to_le32 ( 7 << 21);
+       
+       /* Remember last used (processed) TD phys. addr. */
 -            grub_ohci_free_tds (o,
 -              grub_ohci_td_phys2virt(o,
 -                grub_le_to_cpu32 (td_head_virt->next_td) ) );
++      cdata->td_last_phys = grub_ohci_td_virt2phys (o, cdata->td_current_virt);
+       
+       /* Allocate next TD */
+       td_next_virt = grub_ohci_alloc_td (o);
+       if (!td_next_virt) /* No free TD, cancel transfer and free TDs except head TD */
+         {
+           if (i) /* if i==0 we have nothing to free... */
 -          grub_memset ( (void*)td_head_virt, 0,
++            grub_ohci_free_tds (o, grub_ohci_td_phys2virt(o,
++                                                        grub_le_to_cpu32 (cdata->td_head_virt->next_td)));
+           /* Reset head TD */
 -      td_current_virt->link_td = (grub_uint32_t) td_next_virt;
 -      td_current_virt->next_td = grub_cpu_to_le32 (
 -                                   grub_ohci_td_virt2phys (o,
 -                                     td_next_virt) );
 -      td_next_virt->prev_td_phys = grub_ohci_td_virt2phys (o,
 -                                td_current_virt);
 -      td_current_virt = td_next_virt;
++          grub_memset ( (void*)cdata->td_head_virt, 0,
+                         sizeof(struct grub_ohci_td) );
+           grub_dprintf ("ohci", "Fatal: No free TD !");
++        grub_free (cdata);
+           return GRUB_USB_ERR_INTERNAL;
+         }
+       /* Chain TDs */
 -                td_current_virt);
++      cdata->td_current_virt->link_td = (grub_uint32_t) td_next_virt;
++      cdata->td_current_virt->next_td
++      = grub_cpu_to_le32 (grub_ohci_td_virt2phys (o, td_next_virt));
++      td_next_virt->prev_td_phys
++      = grub_ohci_td_virt2phys (o, cdata->td_current_virt);
++      cdata->td_current_virt = td_next_virt;
+     }
+   grub_dprintf ("ohci", "Tail TD (not processed) = %p\n",
 -  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
++                cdata->td_current_virt);
+   
+   /* Setup the Endpoint Descriptor for transfer.  */
+   /* First set necessary fields in TARGET but keep (or set) skip bit */
+   /* Note: It could be simpler if speed, format and max. packet
+    * size never change after first allocation of ED.
+    * But unfortunately max. packet size may change during initial
+    * setup sequence and we must handle it. */
 -  ed_virt->td_tail
 -    = grub_cpu_to_le32 (grub_ohci_td_virt2phys (o, td_current_virt));
++  cdata->ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
+   /* Set td_tail */
 -  ed_virt->target = grub_cpu_to_le32 (target);
++  cdata->ed_virt->td_tail
++    = grub_cpu_to_le32 (grub_ohci_td_virt2phys (o, cdata->td_current_virt));
+   /* Now reset skip bit */
 -  /* Safety measure to avoid a hang. */
 -  maxtime = grub_get_time_ms () + timeout;
 -      
 -  /* Wait until the transfer is completed or STALLs.  */
 -  do
 -    {
 -      /* Check transfer status */
 -      intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -      if (!o->bad_OHCI && (intstatus & 0x2) != 0)
 -        {
 -          /* Remember last successful TD */
 -          tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
 -          /* Reset DoneHead */
 -        o->hcca->donehead = 0;
 -          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
 -          /* Read back of register should ensure it is really written */
 -          grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -          /* if TD is last, finish */
 -          if (tderr_phys == td_last_phys)
 -            {
 -              if (grub_le_to_cpu32 (ed_virt->td_head) & 1)
 -                err_halt = 1;
 -              break;
 -            }
 -        continue;
 -        }
 -
 -      if ((intstatus & 0x10) != 0)
 -        { /* Unrecoverable error - only reset can help...! */
 -          err_unrec = 1;
 -          break;
 -        }
++  cdata->ed_virt->target = grub_cpu_to_le32 (target);
+   /* ed_virt->td_head = grub_cpu_to_le32 (td_head); Must not be changed, it is maintained by OHCI */
+   /* ed_virt->next_ed = grub_cpu_to_le32 (0); Handled by grub_ohci_find_ed, do not change ! */
+   grub_dprintf ("ohci", "program OHCI\n");
+   /* Program the OHCI to actually transfer.  */
+   switch (transfer->type)
+     {
+     case GRUB_USB_TRANSACTION_TYPE_BULK:
+       {
+       grub_dprintf ("ohci", "BULK list filled\n");
+       /* Set BulkListFilled.  */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 2);
+       /* Read back of register should ensure it is really written */
+       grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+       break;
+       }
+     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
+       {
+       grub_dprintf ("ohci", "CONTROL list filled\n");
+       /* Set ControlListFilled.  */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 1);
+       /* Read back of register should ensure it is really written */
+       grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+       break;
+       }
+     }
 -      /* Detected a HALT.  */
 -      if (err_halt || (grub_le_to_cpu32 (ed_virt->td_head) & 1))
 -        {
 -          err_halt = 1;
 -          /* ED is halted, but donehead event can happened in the meantime */
 -          intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -          if (!o->bad_OHCI && (intstatus & 0x2) != 0)
 -            /* Don't break loop now, first do donehead action(s) */
 -            continue;
 -          break;
 -        }
++  transfer->controller_data = cdata;
 -      /* bad OHCI handling */
 -      if ( (grub_le_to_cpu32 (ed_virt->td_head) & ~0xf) ==
 -           (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf) ) /* Empty ED */
 -        {
 -          if (o->bad_OHCI) /* Bad OHCI detected previously */
 -            {
 -              /* Try get last successful TD. */
 -              tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
 -              if (tderr_phys)/* Reset DoneHead if we were successful */
 -                {
 -                  o->hcca->donehead = 0;
 -                  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
 -                  /* Read back of register should ensure it is really written */
 -                  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -                }
 -              /* Check the HALT bit */
 -              if (grub_le_to_cpu32 (ed_virt->td_head) & 1)
 -                err_halt = 1;
 -              break;
 -            }
 -          else /* Detection of bad OHCI */
 -            /* We should wait short time (~2ms) before we say that
 -             * it is bad OHCI to prevent some hazard -
 -             * donehead can react in the meantime. This waiting is done
 -             * only once per OHCI driver "live cycle". */
 -            if (!bad_OHCI_delay) /* Set delay time */
 -              bad_OHCI_delay = grub_get_time_ms () + 2;
 -            else if (grub_get_time_ms () >= bad_OHCI_delay)
 -              o->bad_OHCI = 1;
 -          continue;
 -        }
 -        
 -      /* Timeout ? */
 -      if (grub_get_time_ms () > maxtime)
 -              {
 -                err_timeout = 1;
 -                break;
 -              }
 -
 -      grub_cpu_idle ();
 -    }
 -  while (1);
++  return GRUB_USB_ERR_NONE;
++}
 -  target = grub_le_to_cpu32 ( ed_virt->target );
 -  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
++static void
++pre_finish_transfer (grub_usb_controller_t dev,
++                   grub_usb_transfer_t transfer)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++  grub_uint32_t target;
++  grub_uint32_t status;
++  grub_uint32_t control;
++  grub_uint32_t intstatus;
+   /* There are many ways how the loop above can finish:
+    * - normally without any error via INTSTATUS WDH bit
+    *   : tderr_phys == td_last_phys, td_head == td_tail
+    * - normally with error via HALT bit in ED TD HEAD
+    *   : td_head = next TD after TD with error
+    *   : tderr_phys = last processed and retired TD with error,
+    *     i.e. should be != 0
+    *   : if bad_OHCI == TRUE, tderr_phys will be probably invalid
+    * - unrecoverable error - I never seen it but it could be
+    *   : err_unrec == TRUE, other values can contain anything...
+    * - timeout, it can be caused by:
+    *  -- bad USB device - some devices have some bugs, see Linux source
+    *     and related links
+    *  -- bad OHCI controller - e.g. lost interrupts or does not set
+    *     proper bits in INTSTATUS when real IRQ not enabled etc.,
+    *     see Linux source and related links
+    *     One known bug is handled - if transfer finished
+    *     successfully (i.e. HEAD==TAIL, last transfer TD is retired,
+    *     HALT bit is not set) and WDH bit is not set in INTSTATUS - in
+    *     this case we set o->bad_OHCI=TRUE and do alternate loop
+    *     and error handling - but there is problem how to find retired
+    *     TD with error code if HALT occurs and if DONEHEAD is not
+    *     working - we need to find TD previous to current ED HEAD
+    *  -- bad code of this driver or some unknown reasons - :-(
+    *     it can be e.g. bad handling of EDs/TDs/toggle bit...
+    */
+   /* Remember target for debug and set skip flag in ED */
+   /* It should be normaly not necessary but we need it at least
+    * in case of timeout */
 -              intstatus, tderr_phys, td_last_phys);
 -  grub_dprintf ("ohci", "err_unrec=%d, err_timeout=%d \n\t\t err_halt=%d, bad_OHCI=%d\n",
 -              err_unrec, err_timeout, err_halt, o->bad_OHCI);
++  target = grub_le_to_cpu32 ( cdata->ed_virt->target );
++  cdata->ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
+   /* Read registers for debug - they should be read now because
+    * debug prints case unwanted delays, so something can happen
+    * in the meantime... */
+   control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+   intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+   /* Now print debug values - to have full info what happened */
+   grub_dprintf ("ohci", "loop finished: control=0x%02x status=0x%02x\n",
+               control, status);
+   grub_dprintf ("ohci", "intstatus=0x%02x \n\t\t tderr_phys=0x%02x, td_last_phys=0x%02x\n",
 -                grub_le_to_cpu32 (ed_virt->td_head),
 -                grub_le_to_cpu32 (ed_virt->td_tail) );
++              intstatus, cdata->tderr_phys, cdata->td_last_phys);
+   grub_dprintf ("ohci", "TARGET=0x%02x, HEAD=0x%02x, TAIL=0x%02x\n",
+                 target,
 -  if (!err_halt && !err_unrec && !err_timeout) /* normal finish */
++                grub_le_to_cpu32 (cdata->ed_virt->td_head),
++                grub_le_to_cpu32 (cdata->ed_virt->td_tail) );
++
++}
++
++static void
++finish_transfer (grub_usb_controller_t dev,
++               grub_usb_transfer_t transfer)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++
++  /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
++  cdata->ed_virt->td_head = grub_cpu_to_le32 (grub_le_to_cpu32 (cdata->ed_virt->td_tail) & ~0xf); 
++
++  /* At this point always should be:
++   * ED has skip bit set and halted or empty or after next SOF,
++   *    i.e. it is safe to free all TDs except last not processed
++   * ED HEAD == TAIL == phys. addr. of td_current_virt */
++
++  /* Reset DoneHead - sanity cleanup */
++  o->hcca->donehead = 0;
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
++  /* Read back of register should ensure it is really written */
++  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -      /* Simple workaround if donehead is not working */
 -      if (o->bad_OHCI &&
 -           ( !tderr_phys || (tderr_phys != td_last_phys) ) )
 -        {
 -          grub_dprintf ("ohci", "normal finish, but tderr_phys corrected\n");
 -          tderr_phys = td_last_phys;
 -          /* I hope we can do it as transfer (most probably) finished OK */
 -        }
 -      /* Prepare pointer to last processed TD */
 -      tderr_virt = grub_ohci_td_phys2virt (o, tderr_phys);
 -      /* Set index of last processed TD */
 -      if (tderr_virt)
 -        transfer->last_trans = tderr_virt->tr_index;
 -      else
 -        transfer->last_trans = -1;
 -      *actual = transfer->size + 1;
++  /* Un-chainig of last TD */
++  if (cdata->td_current_virt->prev_td_phys)
+     {
 -  else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */
 -    {
 -      /* First we must get proper tderr_phys value */
 -      if (o->bad_OHCI) /* In case of bad_OHCI tderr_phys can be wrong */
 -        {
 -          if ( tderr_phys ) /* check if tderr_phys points to TD with error */
 -            errcode = grub_le_to_cpu32 ( grub_ohci_td_phys2virt (o,
 -                                           tderr_phys)->token )
 -                      >> 28;
 -          if ( !tderr_phys || !errcode ) /* tderr_phys not valid or points to wrong TD */
 -            { /* Retired TD with error should be previous TD to ED->td_head */
 -              tderr_phys = grub_ohci_td_phys2virt (o,
 -                             grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf )
 -                           ->prev_td_phys;
 -            }
 -        }
++      grub_ohci_td_t td_prev_virt
++      = grub_ohci_td_phys2virt (o, cdata->td_current_virt->prev_td_phys);
++      
++      if (cdata->td_current_virt == (grub_ohci_td_t) td_prev_virt->link_td)
++        td_prev_virt->link_td = 0;
+     }
 -      /* Even if we have "good" OHCI, in some cases
 -       * tderr_phys can be zero, check it */
 -      else if ( !tderr_phys )
 -        { /* Retired TD with error should be previous TD to ED->td_head */
 -          tderr_phys = grub_ohci_td_phys2virt (o,
 -                         grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf )
 -                       ->prev_td_phys;
 -        }
++  grub_dprintf ("ohci", "OHCI finished, freeing\n");
++  grub_ohci_free_tds (o, cdata->td_head_virt);
++}
 -      /* Prepare pointer to last processed TD and get error code */
 -      tderr_virt = grub_ohci_td_phys2virt (o, tderr_phys);
 -      /* Set index of last processed TD */
 -      if (tderr_virt)
 -        {
 -          errcode = grub_le_to_cpu32 ( tderr_virt->token ) >> 28;
 -          transfer->last_trans = tderr_virt->tr_index;
 -        }
 -      else
 -        transfer->last_trans = -1;
++static grub_usb_err_t
++parse_halt (grub_usb_controller_t dev,
++          grub_usb_transfer_t transfer,
++          grub_size_t *actual)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++  grub_uint8_t errcode = 0;
++  grub_usb_err_t err = GRUB_USB_ERR_NAK;
++  grub_ohci_td_t tderr_virt = NULL;
 -      /* Evaluation of error code */
 -      grub_dprintf ("ohci", "OHCI tderr_phys=0x%02x, errcode=0x%02x\n",
 -                    tderr_phys, errcode);
 -      switch (errcode)
 -      {
 -      case 0:
 -        /* XXX: Should not happen!  */
 -        grub_error (GRUB_ERR_IO, "OHCI failed without reporting the reason");
 -        err = GRUB_USB_ERR_INTERNAL;
 -        break;
 -
 -      case 1:
 -        /* XXX: CRC error.  */
 -        err = GRUB_USB_ERR_TIMEOUT;
 -        break;
 -
 -      case 2:
 -        err = GRUB_USB_ERR_BITSTUFF;
 -        break;
 -
 -      case 3:
 -        /* XXX: Data Toggle error.  */
 -        err = GRUB_USB_ERR_DATA;
 -        break;
 -
 -      case 4:
 -        err = GRUB_USB_ERR_STALL;
 -        break;
 -
 -      case 5:
 -        /* XXX: Not responding.  */
 -        err = GRUB_USB_ERR_TIMEOUT;
 -        break;
 -
 -      case 6:
 -        /* XXX: PID Check bits failed.  */
 -        err = GRUB_USB_ERR_BABBLE;
 -        break;
 -
 -      case 7:
 -        /* XXX: PID unexpected failed.  */
 -        err = GRUB_USB_ERR_BABBLE;
 -        break;
 -
 -      case 8:
 -        /* XXX: Data overrun error.  */
 -        err = GRUB_USB_ERR_DATA;
 -        grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
 -                      tderr_virt, tderr_virt->tr_index);
 -        break;
 -
 -      case 9:
 -        /* XXX: Data underrun error.  */
 -        grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
 -                      tderr_virt, tderr_virt->tr_index);
 -        if (transfer->last_trans == -1)
 -          break;
 -        *actual = transfer->transactions[transfer->last_trans].size
 -          - (grub_le_to_cpu32 (tderr_virt->buffer_end)
 -             - grub_le_to_cpu32 (tderr_virt->buffer))
 -          + transfer->transactions[transfer->last_trans].preceding;
 -        break;
 -
 -      case 10:
 -        /* XXX: Reserved.  */
 -        err = GRUB_USB_ERR_NAK;
 -        break;
 -
 -      case 11:
 -        /* XXX: Reserved.  */
 -        err = GRUB_USB_ERR_NAK;
 -        break;
 -
 -      case 12:
 -        /* XXX: Buffer overrun.  */
 -        err = GRUB_USB_ERR_DATA;
 -        break;
 -
 -      case 13:
 -        /* XXX: Buffer underrun.  */
 -        err = GRUB_USB_ERR_DATA;
 -        break;
 -
 -      default:
 -        err = GRUB_USB_ERR_NAK;
 -        break;
++  *actual = 0;
 -        
 -  else if (err_unrec)      
++  pre_finish_transfer (dev, transfer);
++
++  /* First we must get proper tderr_phys value */
++  if (o->bad_OHCI) /* In case of bad_OHCI tderr_phys can be wrong */
++    {
++      if (cdata->tderr_phys) /* check if tderr_phys points to TD with error */
++      errcode = grub_le_to_cpu32 (grub_ohci_td_phys2virt (o,
++                                                          cdata->tderr_phys)->token)
++        >> 28;
++      if ( !cdata->tderr_phys || !errcode ) /* tderr_phys not valid or points to wrong TD */
++      { /* Retired TD with error should be previous TD to ED->td_head */
++        cdata->tderr_phys = grub_ohci_td_phys2virt (o,
++                                                    grub_le_to_cpu32 (cdata->ed_virt->td_head) & ~0xf )
++          ->prev_td_phys;
+       }
++    }
++  /* Even if we have "good" OHCI, in some cases
++   * tderr_phys can be zero, check it */
++  else if (!cdata->tderr_phys)
++    /* Retired TD with error should be previous TD to ED->td_head */
++    cdata->tderr_phys 
++      = grub_ohci_td_phys2virt (o,
++                              grub_le_to_cpu32 (cdata->ed_virt->td_head)
++                              & ~0xf)->prev_td_phys;
++    
++  /* Prepare pointer to last processed TD and get error code */
++  tderr_virt = grub_ohci_td_phys2virt (o, cdata->tderr_phys);
++  /* Set index of last processed TD */
++  if (tderr_virt)
++    {
++      errcode = grub_le_to_cpu32 (tderr_virt->token) >> 28;
++      transfer->last_trans = tderr_virt->tr_index;
+     }
 -      /* Don't try to get error code and last processed TD for proper
 -       * toggle bit value - anything can be invalid */
 -      err = GRUB_USB_ERR_UNRECOVERABLE;
 -      grub_dprintf("ohci", "Unrecoverable error!");
++  else
++    transfer->last_trans = -1;
++  
++  /* Evaluation of error code */
++  grub_dprintf ("ohci", "OHCI tderr_phys=0x%02x, errcode=0x%02x\n",
++              cdata->tderr_phys, errcode);
++  switch (errcode)
+     {
 -      /* Do OHCI reset in case of unrecoverable error - maybe we will need
 -       * do more - re-enumerate bus etc. (?) */
++    case 0:
++      /* XXX: Should not happen!  */
++      grub_error (GRUB_ERR_IO, "OHCI failed without reporting the reason");
++      err = GRUB_USB_ERR_INTERNAL;
++      break;
 -      /* Suspend the OHCI by issuing a reset.  */
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
 -      /* Read back of register should ensure it is really written */
 -      grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 -      grub_millisleep (1);
 -      grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
++    case 1:
++      /* XXX: CRC error.  */
++      err = GRUB_USB_ERR_TIMEOUT;
++      break;
 -      /* Misc. resets. */
 -      o->hcca->donehead = 0;
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 -      /* Read back of register should ensure it is really written */
 -      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++    case 2:
++      err = GRUB_USB_ERR_BITSTUFF;
++      break;
 -      /* Enable the OHCI.  */
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
 -                            (2 << 6)
 -                            | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
 -                            | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
 -    } 
++    case 3:
++      /* XXX: Data Toggle error.  */
++      err = GRUB_USB_ERR_DATA;
++      break;
 -  else if (err_timeout)
 -    {
 -      /* In case of timeout do not detect error from TD */    
 -      err = GRUB_ERR_TIMEOUT;
 -      grub_dprintf("ohci", "Timeout !\n");
++    case 4:
++      err = GRUB_USB_ERR_STALL;
++      break;
 -      /* We should wait for next SOF to be sure that ED is unaccessed
 -       * by OHCI */
 -      /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
 -      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
 -      /* Wait for new SOF */
 -      while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
++    case 5:
++      /* XXX: Not responding.  */
++      err = GRUB_USB_ERR_TIMEOUT;
++      break;
 -      /* Now we must find last processed TD if bad_OHCI == TRUE */
 -      if (o->bad_OHCI)
 -        { /* Retired TD with error should be previous TD to ED->td_head */
 -          tderr_phys = grub_ohci_td_phys2virt (o,
 -                         grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf)
 -                       ->prev_td_phys;
 -        }
 -      tderr_virt = grub_ohci_td_phys2virt (o, tderr_phys);
 -      if (tderr_virt)
 -        transfer->last_trans = tderr_virt->tr_index;
 -      else
 -        transfer->last_trans = -1;
++    case 6:
++      /* XXX: PID Check bits failed.  */
++      err = GRUB_USB_ERR_BABBLE;
++      break;
 -  /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
 -  ed_virt->td_head = grub_cpu_to_le32 (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf); 
++    case 7:
++      /* XXX: PID unexpected failed.  */
++      err = GRUB_USB_ERR_BABBLE;
++      break;
++
++    case 8:
++      /* XXX: Data overrun error.  */
++      err = GRUB_USB_ERR_DATA;
++      grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
++                  tderr_virt, tderr_virt->tr_index);
++      break;
++
++    case 9:
++      /* XXX: Data underrun error.  */
++      grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
++                  tderr_virt, tderr_virt->tr_index);
++      if (transfer->last_trans == -1)
++      break;
++      *actual = transfer->transactions[transfer->last_trans].size
++      - (grub_le_to_cpu32 (tderr_virt->buffer_end)
++         - grub_le_to_cpu32 (tderr_virt->buffer))
++      + transfer->transactions[transfer->last_trans].preceding;
++      err = GRUB_USB_ERR_NONE;
++      break;
++
++    case 10:
++      /* XXX: Reserved.  */
++      err = GRUB_USB_ERR_NAK;
++      break;
++
++    case 11:
++      /* XXX: Reserved.  */
++      err = GRUB_USB_ERR_NAK;
++      break;
++
++    case 12:
++      /* XXX: Buffer overrun.  */
++      err = GRUB_USB_ERR_DATA;
++      break;
++
++    case 13:
++      /* XXX: Buffer underrun.  */
++      err = GRUB_USB_ERR_DATA;
++      break;
++
++    default:
++      err = GRUB_USB_ERR_NAK;
++      break;
+     }
 -  /* At this point always should be:
 -   * ED has skip bit set and halted or empty or after next SOF,
 -   *    i.e. it is safe to free all TDs except last not processed
 -   * ED HEAD == TAIL == phys. addr. of td_current_virt */
++  finish_transfer (dev, transfer);
 -  /* Reset DoneHead - sanity cleanup */
++  return err;
++}
 -  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
++static grub_usb_err_t
++parse_success (grub_usb_controller_t dev,
++             grub_usb_transfer_t transfer,
++             grub_size_t *actual)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++  grub_ohci_td_t tderr_virt = NULL;
++
++  pre_finish_transfer (dev, transfer);
++
++  /* Simple workaround if donehead is not working */
++  if (o->bad_OHCI &&
++      (!cdata->tderr_phys || (cdata->tderr_phys != cdata->td_last_phys)))
++    {
++      grub_dprintf ("ohci", "normal finish, but tderr_phys corrected\n");
++      cdata->tderr_phys = cdata->td_last_phys;
++      /* I hope we can do it as transfer (most probably) finished OK */
++    }
++  /* Prepare pointer to last processed TD */
++  tderr_virt = grub_ohci_td_phys2virt (o, cdata->tderr_phys);
++  /* Set index of last processed TD */
++  if (tderr_virt)
++    transfer->last_trans = tderr_virt->tr_index;
++  else
++    transfer->last_trans = -1;
++  *actual = transfer->size + 1;
++
++  finish_transfer (dev, transfer);
++
++  return GRUB_USB_ERR_NONE;
++}
++
++static grub_usb_err_t
++parse_unrec (grub_usb_controller_t dev,
++           grub_usb_transfer_t transfer,
++           grub_size_t *actual)
++{
++  struct grub_ohci *o = dev->data;
++
++  *actual = 0;
++
++  pre_finish_transfer (dev, transfer);
++
++  /* Don't try to get error code and last processed TD for proper
++   * toggle bit value - anything can be invalid */
++  grub_dprintf("ohci", "Unrecoverable error!");
++
++  /* Do OHCI reset in case of unrecoverable error - maybe we will need
++   * do more - re-enumerate bus etc. (?) */
++
++  /* Suspend the OHCI by issuing a reset.  */
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
++  /* Read back of register should ensure it is really written */
++  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
++  grub_millisleep (1);
++  grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
++
++  /* Misc. resets. */
+   o->hcca->donehead = 0;
 -  /* Un-chainig of last TD */
 -  if (td_current_virt->prev_td_phys)
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+   /* Read back of register should ensure it is really written */
+   grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 -      grub_ohci_td_t td_prev_virt
 -                = grub_ohci_td_phys2virt (o, td_current_virt->prev_td_phys);
++  /* Enable the OHCI.  */
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
++                      (2 << 6)
++                      | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
++                      | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
++  finish_transfer (dev, transfer);
++
++  return GRUB_USB_ERR_UNRECOVERABLE;
++}
++
++static grub_usb_err_t
++grub_ohci_check_transfer (grub_usb_controller_t dev,
++                        grub_usb_transfer_t transfer,
++                        grub_size_t *actual)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++  grub_uint32_t intstatus;
++
++  /* Check transfer status */
++  intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++  if (!o->bad_OHCI && (intstatus & 0x2) != 0)
+     {
 -      td_next_virt = (grub_ohci_td_t) td_prev_virt->link_td;
 -      if (td_current_virt == td_next_virt)
 -        td_prev_virt->link_td = 0;
++      /* Remember last successful TD */
++      cdata->tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
++      /* Reset DoneHead */
++      o->hcca->donehead = 0;
++      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
++      /* Read back of register should ensure it is really written */
++      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++      /* if TD is last, finish */
++      if (cdata->tderr_phys == cdata->td_last_phys)
++      {
++        if (grub_le_to_cpu32 (cdata->ed_virt->td_head) & 1)
++          return parse_halt (dev, transfer, actual);
++        else
++          return parse_success (dev, transfer, actual);
++      }
++      return GRUB_USB_ERR_WAIT;
++    }
 -  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x, errcode=0x%02x\n",
 -                err, errcode);
 -  grub_ohci_free_tds (o, td_head_virt);
++  if ((intstatus & 0x10) != 0)
++    /* Unrecoverable error - only reset can help...! */
++    return parse_unrec (dev, transfer, actual);
++
++  /* Detected a HALT.  */
++  if ((grub_le_to_cpu32 (cdata->ed_virt->td_head) & 1))
++    {
++      /* ED is halted, but donehead event can happened in the meantime */
++      intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++      if (!o->bad_OHCI && (intstatus & 0x2) != 0)
++      {
++        /* Remember last successful TD */
++        cdata->tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
++        /* Reset DoneHead */
++        o->hcca->donehead = 0;
++        grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
++        /* Read back of register should ensure it is really written */
++        grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++        /* if TD is last, finish */
++      }
++      return parse_halt (dev, transfer, actual);
+     }
 -  return err;
++  /* bad OHCI handling */
++  if ( (grub_le_to_cpu32 (cdata->ed_virt->td_head) & ~0xf) ==
++       (grub_le_to_cpu32 (cdata->ed_virt->td_tail) & ~0xf) ) /* Empty ED */
++    {
++      if (o->bad_OHCI) /* Bad OHCI detected previously */
++      {
++        /* Try get last successful TD. */
++        cdata->tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
++        if (cdata->tderr_phys)/* Reset DoneHead if we were successful */
++          {
++            o->hcca->donehead = 0;
++            grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
++            /* Read back of register should ensure it is really written */
++            grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
++          }
++        /* Check the HALT bit */
++        if (grub_le_to_cpu32 (cdata->ed_virt->td_head) & 1)
++          return parse_halt (dev, transfer, actual);
++        else
++          return parse_success (dev, transfer, actual);
++      }
++      else /* Detection of bad OHCI */
++      /* We should wait short time (~2ms) before we say that
++       * it is bad OHCI to prevent some hazard -
++       * donehead can react in the meantime. This waiting is done
++       * only once per OHCI driver "live cycle". */
++      if (!cdata->bad_OHCI_delay) /* Set delay time */
++        cdata->bad_OHCI_delay = grub_get_time_ms () + 2;
++      else if (grub_get_time_ms () >= cdata->bad_OHCI_delay)
++        o->bad_OHCI = 1;
++      return GRUB_USB_ERR_WAIT;
++    }
 -  /* Connect Status Change bit - it detects change of connection */
 -  *changed = ((status & GRUB_OHCI_RESET_CONNECT_CHANGE) != 0);
++  return GRUB_USB_ERR_WAIT;
++}
++
++static grub_usb_err_t
++grub_ohci_cancel_transfer (grub_usb_controller_t dev,
++                         grub_usb_transfer_t transfer)
++{
++  struct grub_ohci *o = dev->data;
++  struct grub_ohci_transfer_controller_data *cdata = transfer->controller_data;
++  grub_ohci_td_t tderr_virt = NULL;
++
++  pre_finish_transfer (dev, transfer);
++
++  grub_dprintf("ohci", "Timeout !\n");
++
++  /* We should wait for next SOF to be sure that ED is unaccessed
++   * by OHCI */
++  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
++  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
++  /* Wait for new SOF */
++  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
++
++  /* Now we must find last processed TD if bad_OHCI == TRUE */
++  if (o->bad_OHCI)
++    /* Retired TD with error should be previous TD to ED->td_head */
++    cdata->tderr_phys
++      = grub_ohci_td_phys2virt (o, grub_le_to_cpu32 (cdata->ed_virt->td_head)
++                              & ~0xf)->prev_td_phys;
++    
++  tderr_virt = grub_ohci_td_phys2virt (o,cdata-> tderr_phys);
++  if (tderr_virt)
++    transfer->last_trans = tderr_virt->tr_index;
++  else
++    transfer->last_trans = -1;
++
++  finish_transfer (dev, transfer);
++
++  return GRUB_USB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_ohci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable)
+ {
+    struct grub_ohci *o = (struct grub_ohci *) dev->data;
+    grub_uint64_t endtime;
+    grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
+                  grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+    if (!enable) /* We don't need reset port */
+      {
+        /* Disable the port and wait for it. */
+        grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+                              GRUB_OHCI_CLEAR_PORT_ENABLE);
+        endtime = grub_get_time_ms () + 1000;
+        while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)
+                & (1 << 1)))
+          if (grub_get_time_ms () > endtime)
+            return grub_error (GRUB_ERR_IO, "OHCI Timed out - disable");
+        grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
+          grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+        return GRUB_ERR_NONE;
+      }
+      
+    /* Reset the port */
+    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+                        GRUB_OHCI_SET_PORT_RESET);
+    grub_millisleep (50); /* For root hub should be nominaly 50ms */
+  
+     /* End the reset signaling.  */
+    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+                        GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
+    grub_millisleep (10);
+    /* Enable the port and wait for it. */
+    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+                          GRUB_OHCI_SET_PORT_ENABLE);
+    endtime = grub_get_time_ms () + 1000;
+    while (! (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)
+            & (1 << 1)))
+      if (grub_get_time_ms () > endtime)
+        return grub_error (GRUB_ERR_IO, "OHCI Timed out - enable");
+    grub_millisleep (10);
+    /* Reset bit Connect Status Change */
+    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+                          GRUB_OHCI_RESET_CONNECT_CHANGE);
+    grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
+                grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+  
+    return GRUB_ERR_NONE;
+ }
+ static grub_usb_speed_t
+ grub_ohci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
+ {
+    struct grub_ohci *o = (struct grub_ohci *) dev->data;
+    grub_uint32_t status;
+    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+    grub_dprintf ("ohci", "detect_dev status=0x%02x\n", status);
 -  .transfer = grub_ohci_transfer,
++   /* Connect Status Change bit - it detects change of connection */
++   if (status & GRUB_OHCI_RESET_CONNECT_CHANGE)
++     {
++       *changed = 1;
++       /* Reset bit Connect Status Change */
++       grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
++                           GRUB_OHCI_RESET_CONNECT_CHANGE);
++     }
++   else
++     *changed = 0;
+    if (! (status & 1))
+      return GRUB_USB_SPEED_NONE;
+    else if (status & (1 << 9))
+      return GRUB_USB_SPEED_LOW;
+    else
+      return GRUB_USB_SPEED_FULL;
+ }
+ static int
+ grub_ohci_hubports (grub_usb_controller_t dev)
+ {
+   struct grub_ohci *o = (struct grub_ohci *) dev->data;
+   grub_uint32_t portinfo;
+   portinfo = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA);
+   grub_dprintf ("ohci", "root hub ports=%d\n", portinfo & 0xFF);
+   return portinfo & 0xFF;
+ }
+ static grub_err_t
+ grub_ohci_fini_hw (int noreturn __attribute__ ((unused)))
+ {
+   struct grub_ohci *o;
+   for (o = ohci; o; o = o->next)
+     {
+       int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff;
+       grub_uint64_t maxtime;
+       /* Set skip in all EDs */
+       if (o->ed_bulk)
+         for (i=0; i < GRUB_OHCI_BULK_EDS; i++)
+           o->ed_bulk[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */
+       if (o->ed_ctrl)
+         for (i=0; i < GRUB_OHCI_CTRL_EDS; i++)
+           o->ed_ctrl[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */
+       /* We should wait for next SOF to be sure that all EDs are
+        * unaccessed by OHCI. But OHCI can be non-functional, so
+        * more than 1ms timeout have to be applied. */
+       /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+       maxtime = grub_get_time_ms () + 2;
+       /* Wait for new SOF or timeout */
+       while ( ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4)
+                  == 0) || (grub_get_time_ms () >= maxtime) );
+       for (i = 0; i < nports; i++)
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i,
+                             GRUB_OHCI_CLEAR_PORT_ENABLE);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1);
+       grub_millisleep (1);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, 0);
+       /* Read back of register should ensure it is really written */
+       grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+ #if 0 /* Is this necessary before booting? Probably not .(?)
+        * But it must be done if module is removed ! (Or not ?)
+        * How to do it ? - Probably grub_ohci_restore_hw should be more
+        * complicated. (?)
+        * (If we do it, we need to reallocate EDs and TDs in function
+        * grub_ohci_restore_hw ! */
+       /* Free allocated EDs and TDs */
+       grub_dma_free (o->td_chunk);
+       grub_dma_free (o->ed_bulk_chunk);
+       grub_dma_free (o->ed_ctrl_chunk);
+       grub_dma_free (o->hcca_chunk);
+ #endif
+     }
+   grub_millisleep (10);
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_ohci_restore_hw (void)
+ {
+   struct grub_ohci *o;
+   for (o = ohci; o; o = o->next)
+     {
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
+       o->hcca->donehead = 0;
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+       /* Read back of register should ensure it is really written */
+       grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+       /* Enable the OHCI.  */
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                             (2 << 6)
+                             | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
+                             | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
+     }
+   return GRUB_ERR_NONE;
+ }
\f
+ static struct grub_usb_controller_dev usb_controller =
+ {
+   .name = "ohci",
+   .iterate = grub_ohci_iterate,
++  .setup_transfer = grub_ohci_setup_transfer,
++  .check_transfer = grub_ohci_check_transfer,
++  .cancel_transfer = grub_ohci_cancel_transfer,
+   .hubports = grub_ohci_hubports,
+   .portstatus = grub_ohci_portstatus,
+   .detect_dev = grub_ohci_detect_dev
+ };
+ GRUB_MOD_INIT(ohci)
+ {
+   grub_ohci_inithw ();
+   grub_usb_controller_dev_register (&usb_controller);
+   grub_loader_register_preboot_hook (grub_ohci_fini_hw, grub_ohci_restore_hw,
+                                    GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
+ }
+ GRUB_MOD_FINI(ohci)
+ {
+   grub_ohci_fini_hw (0);
+   grub_usb_controller_dev_unregister (&usb_controller);
+ }
index 0000000000000000000000000000000000000000,0bba24b54b6251c45398174cc5835c1bcbce67f5..b719051d624fa8002c235e21d6afef66661c6fd6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,731 +1,802 @@@
 -  /* 256 Queue Heads.  */
+ /* uhci.c - UHCI Support.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/mm.h>
+ #include <grub/misc.h>
+ #include <grub/usb.h>
+ #include <grub/usbtrans.h>
+ #include <grub/pci.h>
+ #include <grub/i386/io.h>
+ #include <grub/time.h>
+ #define GRUB_UHCI_IOMASK      (0x7FF << 5)
++#define N_QH  256
++
+ typedef enum
+   {
+     GRUB_UHCI_REG_USBCMD = 0x00,
+     GRUB_UHCI_REG_FLBASEADD = 0x08,
+     GRUB_UHCI_REG_PORTSC1 = 0x10,
+     GRUB_UHCI_REG_PORTSC2 = 0x12
+   } grub_uhci_reg_t;
+ #define GRUB_UHCI_LINK_TERMINATE      1
+ #define GRUB_UHCI_LINK_QUEUE_HEAD     2
++enum
++  {
++    GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED = 0x0002,
++    GRUB_UHCI_REG_PORTSC_PORT_ENABLED    = 0x0004,
++    GRUB_UHCI_REG_PORTSC_RESUME          = 0x0040,
++    GRUB_UHCI_REG_PORTSC_RESET           = 0x0200,
++    GRUB_UHCI_REG_PORTSC_SUSPEND         = 0x1000,
++    GRUB_UHCI_REG_PORTSC_RW = GRUB_UHCI_REG_PORTSC_PORT_ENABLED
++    | GRUB_UHCI_REG_PORTSC_RESUME | GRUB_UHCI_REG_PORTSC_RESET
++    | GRUB_UHCI_REG_PORTSC_SUSPEND
++  };
++
+ /* UHCI Queue Head.  */
+ struct grub_uhci_qh
+ {
+   /* Queue head link pointer which points to the next queue head.  */
+   grub_uint32_t linkptr;
+   /* Queue element link pointer which points to the first data object
+      within the queue.  */
+   grub_uint32_t elinkptr;
+   /* Queue heads are aligned on 16 bytes, pad so a queue head is 16
+      bytes so we can store many in a 4K page.  */
+   grub_uint8_t pad[8];
+ } __attribute__ ((packed));
+ /* UHCI Transfer Descriptor.  */
+ struct grub_uhci_td
+ {
+   /* Pointer to the next TD in the list.  */
+   grub_uint32_t linkptr;
+   /* Control and status bits.  */
+   grub_uint32_t ctrl_status;
+   /* All information required to transfer the Token packet.  */
+   grub_uint32_t token;
+   /* A pointer to the data buffer, UHCI requires this pointer to be 32
+      bits.  */
+   grub_uint32_t buffer;
+   /* Another linkptr that is not overwritten by the Host Controller.
+      This is GRUB specific.  */
+   grub_uint32_t linkptr2;
+   /* 3 additional 32 bits words reserved for the Host Controller Driver.  */
+   grub_uint32_t data[3];
+ } __attribute__ ((packed));
+ typedef volatile struct grub_uhci_td *grub_uhci_td_t;
+ typedef volatile struct grub_uhci_qh *grub_uhci_qh_t;
+ struct grub_uhci
+ {
+   int iobase;
+   grub_uint32_t *framelist;
 -  for (i = 0; i < 256; i++)
++  /* N_QH Queue Heads.  */
+   grub_uhci_qh_t qh;
+   /* 256 Transfer Descriptors.  */
+   grub_uhci_td_t td;
+   /* Free Transfer Descriptors.  */
+   grub_uhci_td_t tdfree;
++  int qh_busy[N_QH];
++
+   struct grub_uhci *next;
+ };
+ static struct grub_uhci *uhci;
+ static grub_uint16_t
+ grub_uhci_readreg16 (struct grub_uhci *u, grub_uhci_reg_t reg)
+ {
+   return grub_inw (u->iobase + reg);
+ }
+ #if 0
+ static grub_uint32_t
+ grub_uhci_readreg32 (struct grub_uhci *u, grub_uhci_reg_t reg)
+ {
+   return grub_inl (u->iobase + reg);
+ }
+ #endif
+ static void
+ grub_uhci_writereg16 (struct grub_uhci *u,
+                     grub_uhci_reg_t reg, grub_uint16_t val)
+ {
+   grub_outw (val, u->iobase + reg);
+ }
+ static void
+ grub_uhci_writereg32 (struct grub_uhci *u,
+                   grub_uhci_reg_t reg, grub_uint32_t val)
+ {
+   grub_outl (val, u->iobase + reg);
+ }
+ static grub_err_t
+ grub_uhci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable);
+ /* Iterate over all PCI devices.  Determine if a device is an UHCI
+    controller.  If this is the case, initialize it.  */
+ static int NESTED_FUNC_ATTR
+ grub_uhci_pci_iter (grub_pci_device_t dev,
+                   grub_pci_id_t pciid __attribute__((unused)))
+ {
+   grub_uint32_t class_code;
+   grub_uint32_t class;
+   grub_uint32_t subclass;
+   grub_uint32_t interf;
+   grub_uint32_t base;
+   grub_uint32_t fp;
+   grub_pci_address_t addr;
+   struct grub_uhci *u;
+   int i;
+   addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+   class_code = grub_pci_read (addr) >> 8;
+   interf = class_code & 0xFF;
+   subclass = (class_code >> 8) & 0xFF;
+   class = class_code >> 16;
+   /* If this is not an UHCI controller, just return.  */
+   if (class != 0x0c || subclass != 0x03 || interf != 0x00)
+     return 0;
+   /* Determine IO base address.  */
+   addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG4);
+   base = grub_pci_read (addr);
+   /* Stop if there is no IO space base address defined.  */
+   if (! (base & 1))
+     return 0;
+   /* Allocate memory for the controller and register it.  */
+   u = grub_zalloc (sizeof (*u));
+   if (! u)
+     return 1;
+   u->iobase = base & GRUB_UHCI_IOMASK;
+   /* Reserve a page for the frame list.  */
+   u->framelist = grub_memalign (4096, 4096);
+   if (! u->framelist)
+     goto fail;
+   grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
+               class, subclass, interf, u->iobase, u->framelist);
+   /* The framelist pointer of UHCI is only 32 bits, make sure this
+      code works on on 64 bits architectures.  */
+ #if GRUB_CPU_SIZEOF_VOID_P == 8
+   if ((grub_uint64_t) u->framelist >> 32)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "allocated frame list memory not <4GB");
+       goto fail;
+     }
+ #endif
+   /* The QH pointer of UHCI is only 32 bits, make sure this
+      code works on on 64 bits architectures.  */
+   u->qh = (grub_uhci_qh_t) grub_memalign (4096, 4096);
+   if (! u->qh)
+     goto fail;
+ #if GRUB_CPU_SIZEOF_VOID_P == 8
+   if ((grub_uint64_t) u->qh >> 32)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, "allocated QH memory not <4GB");
+       goto fail;
+     }
+ #endif
+   /* The TD pointer of UHCI is only 32 bits, make sure this
+      code works on on 64 bits architectures.  */
+   u->td = (grub_uhci_td_t) grub_memalign (4096, 4096*2);
+   if (! u->td)
+     goto fail;
+ #if GRUB_CPU_SIZEOF_VOID_P == 8
+   if ((grub_uint64_t) u->td >> 32)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, "allocated TD memory not <4GB");
+       goto fail;
+     }
+ #endif
+   grub_dprintf ("uhci", "QH=%p, TD=%p\n",
+               u->qh, u->td);
+   /* Link all Transfer Descriptors in a list of available Transfer
+      Descriptors.  */
+   for (i = 0; i < 256; i++)
+     u->td[i].linkptr = (grub_uint32_t) &u->td[i + 1];
+   u->td[255 - 1].linkptr = 0;
+   u->tdfree = u->td;
+   /* Make sure UHCI is disabled!  */
+   grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 0);
+   /* Setup the frame list pointers.  Since no isochronous transfers
+      are and will be supported, they all point to the (same!) queue
+      head.  */
+   fp = (grub_uint32_t) u->qh & (~15);
+   /* Mark this as a queue head.  */
+   fp |= 2;
+   for (i = 0; i < 1024; i++)
+     u->framelist[i] = fp;
+   /* Program the framelist address into the UHCI controller.  */
+   grub_uhci_writereg32 (u, GRUB_UHCI_REG_FLBASEADD,
+                       (grub_uint32_t) u->framelist);
+   /* Make the Queue Heads point to each other.  */
 -  /* The last Queue Head should terminate.  256 are too many QHs so
 -     just use 50.  */
 -  u->qh[50 - 1].linkptr = 1;
++  for (i = 0; i < N_QH; i++)
+     {
+       /* Point to the next QH.  */
+       u->qh[i].linkptr = (grub_uint32_t) (&u->qh[i + 1]) & (~15);
+       /* This is a QH.  */
+       u->qh[i].linkptr |= GRUB_UHCI_LINK_QUEUE_HEAD;
+       /* For the moment, do not point to a Transfer Descriptor.  These
+        are set at transfer time, so just terminate it.  */
+       u->qh[i].elinkptr = 1;
+     }
 -grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td,
++  /* The last Queue Head should terminate.  */
++  u->qh[N_QH - 1].linkptr = 1;
+   /* Enable UHCI again.  */
+   grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 1 | (1 << 7));
+   /* UHCI is initialized and ready for transfers.  */
+   grub_dprintf ("uhci", "UHCI initialized\n");
+ #if 0
+   {
+     int i;
+     for (i = 0; i < 10; i++)
+       {
+       grub_uint16_t frnum;
+       frnum = grub_uhci_readreg16 (u, 6);
+       grub_dprintf ("uhci", "Framenum=%d\n", frnum);
+       grub_millisleep (100);
+       }
+   }
+ #endif
+   /* Link to uhci now that initialisation is successful.  */
+   u->next = uhci;
+   uhci = u;
+   return 0;
+  fail:
+   if (u)
+     {
+       grub_free ((void *) u->qh);
+       grub_free (u->framelist);
+     }
+   grub_free (u);
+   return 1;
+ }
+ static void
+ grub_uhci_inithw (void)
+ {
+   grub_pci_iterate (grub_uhci_pci_iter);
+ }
+ static grub_uhci_td_t
+ grub_alloc_td (struct grub_uhci *u)
+ {
+   grub_uhci_td_t ret;
+   /* Check if there is a Transfer Descriptor available.  */
+   if (! u->tdfree)
+     return NULL;
+   ret = u->tdfree;
+   u->tdfree = (grub_uhci_td_t) u->tdfree->linkptr;
+   return ret;
+ }
+ static void
+ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
+ {
+   td->linkptr = (grub_uint32_t) u->tdfree;
+   u->tdfree = td;
+ }
+ static void
 -  for (; i < 255; i++)
++grub_free_queue (struct grub_uhci *u, grub_uhci_qh_t qh, grub_uhci_td_t td,
+                  grub_usb_transfer_t transfer, grub_size_t *actual)
+ {
+   int i; /* Index of TD in transfer */
++  u->qh_busy[qh - u->qh] = 0;
++
+   *actual = 0;
+   
+   /* Free the TDs in this queue and set last_trans.  */
+   for (i=0; td; i++)
+     {
+       grub_uhci_td_t tdprev;
+       /* Check state of TD and possibly set last_trans */
+       if (transfer && (td->linkptr & 1))
+         transfer->last_trans = i;
+       *actual += (td->ctrl_status + 1) & 0x7ff;
+       
+       /* Unlink the queue.  */
+       tdprev = td;
+       td = (grub_uhci_td_t) td->linkptr2;
+       /* Free the TD.  */
+       grub_free_td (u, tdprev);
+     }
+ }
+ static grub_uhci_qh_t
+ grub_alloc_qh (struct grub_uhci *u,
+              grub_transaction_type_t tr __attribute__((unused)))
+ {
+   int i;
+   grub_uhci_qh_t qh;
+   /* Look for a Queue Head for this transfer.  Skip the first QH if
+      this is a Interrupt Transfer.  */
+ #if 0
+   if (tr == GRUB_USB_TRANSACTION_TYPE_INTERRUPT)
+     i = 0;
+   else
+ #endif
+     i = 1;
 -      if (u->qh[i].elinkptr & 1)
++  for (; i < N_QH; i++)
+     {
 -  if (! (qh->elinkptr & 1))
++      if (!u->qh_busy[i])
+       break;
+     }
+   qh = &u->qh[i];
 -grub_uhci_transfer (grub_usb_controller_t dev,
 -                  grub_usb_transfer_t transfer,
 -                  int timeout, grub_size_t *actual)
++  if (i == N_QH)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "no free queue heads available");
+       return NULL;
+     }
++  u->qh_busy[qh - u->qh] = 1;
++
+   return qh;
+ }
+ static grub_uhci_td_t
+ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp,
+                      grub_transfer_type_t type, unsigned int addr,
+                      unsigned int toggle, grub_size_t size,
+                      grub_uint32_t data)
+ {
+   grub_uhci_td_t td;
+   static const unsigned int tf[] = { 0x69, 0xE1, 0x2D };
+   /* XXX: Check if data is <4GB.  If it isn't, just copy stuff around.
+      This is only relevant for 64 bits architectures.  */
+   /* Grab a free Transfer Descriptor and initialize it.  */
+   td = grub_alloc_td (u);
+   if (! td)
+     {
+       grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "no transfer descriptors available for UHCI transfer");
+       return 0;
+     }
+   grub_dprintf ("uhci",
+               "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d data=0x%x td=%p\n",
+               endp, type, addr, toggle, size, data, td);
+   /* Don't point to any TD, just terminate.  */
+   td->linkptr = 1;
+   /* Active!  Only retry a transfer 3 times.  */
+   td->ctrl_status = (1 << 23) | (3 << 27);
+   /* If zero bytes are transmitted, size is 0x7FF.  Otherwise size is
+      size-1.  */
+   if (size == 0)
+     size = 0x7FF;
+   else
+     size = size - 1;
+   /* Setup whatever is required for the token packet.  */
+   td->token = ((size << 21) | (toggle << 19) | (endp << 15)
+              | (addr << 8) | tf[type]);
+   td->buffer = data;
+   return td;
+ }
++struct grub_uhci_transfer_controller_data
++{
++  grub_uhci_qh_t qh;
++  grub_uhci_td_t td_first;
++};
++
+ static grub_usb_err_t
 -  grub_uhci_qh_t qh;
++grub_uhci_setup_transfer (grub_usb_controller_t dev,
++                        grub_usb_transfer_t transfer)
+ {
+   struct grub_uhci *u = (struct grub_uhci *) dev->data;
 -  grub_uhci_td_t td_first = NULL;
+   grub_uhci_td_t td;
 -  grub_usb_err_t err = GRUB_USB_ERR_NONE;
+   grub_uhci_td_t td_prev = NULL;
 -  grub_uint64_t endtime;
+   int i;
 -  *actual = 0;
++  struct grub_uhci_transfer_controller_data *cdata;
 -  qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL);
 -  if (! qh)
 -    return GRUB_USB_ERR_INTERNAL;
++  cdata = grub_malloc (sizeof (*cdata));
++  if (!cdata)
++    return GRUB_USB_ERR_INTERNAL;
++
++  cdata->td_first = NULL;
+   /* Allocate a queue head for the transfer queue.  */
 -      td = grub_uhci_transaction (u, transfer->endpoint, tr->pid,
++  cdata->qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL);
++  if (! cdata->qh)
++    {
++      grub_free (cdata);
++      return GRUB_USB_ERR_INTERNAL;
++    }
+   grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
+   
+   for (i = 0; i < transfer->transcnt; i++)
+     {
+       grub_usb_transaction_t tr = &transfer->transactions[i];
 -        if (td_first)
 -          grub_free_queue (u, td_first, NULL, actual);
++      td = grub_uhci_transaction (u, transfer->endpoint & 15, tr->pid,
+                                 transfer->devaddr, tr->toggle,
+                                 tr->size, tr->data);
+       if (! td)
+       {
++        grub_size_t actual = 0;
+         /* Terminate and free.  */
+         td_prev->linkptr2 = 0;
+         td_prev->linkptr = 1;
 -      if (! td_first)
 -      td_first = td;
++        if (cdata->td_first)
++          grub_free_queue (u, cdata->qh, cdata->td_first, NULL, &actual);
++        grub_free (cdata);
+         return GRUB_USB_ERR_INTERNAL;
+       }
 -  qh->elinkptr = (grub_uint32_t) td_first;
++      if (! cdata->td_first)
++      cdata->td_first = td;
+       else
+       {
+         td_prev->linkptr2 = (grub_uint32_t) td;
+         td_prev->linkptr = (grub_uint32_t) td;
+         td_prev->linkptr |= 4;
+       }
+       td_prev = td;
+     }
+   td_prev->linkptr2 = 0;
+   td_prev->linkptr = 1;
+   grub_dprintf ("uhci", "setup transaction %d\n", transfer->type);
+   /* Link it into the queue and terminate.  Now the transaction can
+      take place.  */
 -  /* Wait until either the transaction completed or an error
 -     occurred.  */
 -  endtime = grub_get_time_ms () + timeout;
 -  for (;;)
 -    {
 -      grub_uhci_td_t errtd;
++  cdata->qh->elinkptr = (grub_uint32_t) cdata->td_first;
+   grub_dprintf ("uhci", "initiate transaction\n");
 -      errtd = (grub_uhci_td_t) (qh->elinkptr & ~0x0f);
++  transfer->controller_data = cdata;
++
++  return GRUB_USB_ERR_NONE;
++}
 -      grub_dprintf ("uhci", ">t status=0x%02x data=0x%02x td=%p\n",
 -                  errtd->ctrl_status, errtd->buffer & (~15), errtd);
++static grub_usb_err_t
++grub_uhci_check_transfer (grub_usb_controller_t dev,
++                        grub_usb_transfer_t transfer,
++                        grub_size_t *actual)
++{
++  struct grub_uhci *u = (struct grub_uhci *) dev->data;
++  grub_uhci_td_t errtd;
++  struct grub_uhci_transfer_controller_data *cdata = transfer->controller_data;
 -      /* Check if the transaction completed.  */
 -      if (qh->elinkptr & 1)
 -      break;
++  *actual = 0;
 -      grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status);
++  errtd = (grub_uhci_td_t) (cdata->qh->elinkptr & ~0x0f);
++  
++  grub_dprintf ("uhci", ">t status=0x%02x data=0x%02x td=%p\n",
++              errtd->ctrl_status, errtd->buffer & (~15), errtd);
 -
++  /* Check if the transaction completed.  */
++  if (cdata->qh->elinkptr & 1)
++    {
++      grub_dprintf ("uhci", "transaction complete\n");
++
++      /* Place the QH back in the free list and deallocate the associated
++       TDs.  */
++      cdata->qh->elinkptr = 1;
++      grub_free_queue (u, cdata->qh, cdata->td_first, transfer, actual);
++      grub_free (cdata);
++      return GRUB_USB_ERR_NONE;
++    }
++
++  grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status);
++
++  if (!(errtd->ctrl_status & (1 << 23)))
++    {
++      grub_usb_err_t err = GRUB_USB_ERR_NONE;
+       /* Check if the endpoint is stalled.  */
+       if (errtd->ctrl_status & (1 << 22))
+       err = GRUB_USB_ERR_STALL;
 -
++      
+       /* Check if an error related to the data buffer occurred.  */
+       if (errtd->ctrl_status & (1 << 21))
+       err = GRUB_USB_ERR_DATA;
 -
++      
+       /* Check if a babble error occurred.  */
+       if (errtd->ctrl_status & (1 << 20))
+       err = GRUB_USB_ERR_BABBLE;
 -
++      
+       /* Check if a NAK occurred.  */
+       if (errtd->ctrl_status & (1 << 19))
+       err = GRUB_USB_ERR_NAK;
 -
++      
+       /* Check if a timeout occurred.  */
+       if (errtd->ctrl_status & (1 << 18))
+       err = GRUB_USB_ERR_TIMEOUT;
 -
++      
+       /* Check if a bitstuff error occurred.  */
+       if (errtd->ctrl_status & (1 << 17))
+       err = GRUB_USB_ERR_BITSTUFF;
 -      goto fail;
++      
+       if (err)
 -      /* Fall through, no errors occurred, so the QH might be
 -       updated.  */
 -      grub_dprintf ("uhci", "transaction fallthrough\n");
++      {
++        grub_dprintf ("uhci", "transaction failed\n");
 -      if (grub_get_time_ms () > endtime)
 -      {
 -        err = GRUB_USB_ERR_STALL;
 -        grub_dprintf ("uhci", "transaction timed out\n");
 -        goto fail;
++        /* Place the QH back in the free list and deallocate the associated
++           TDs.  */
++        cdata->qh->elinkptr = 1;
++        grub_free_queue (u, cdata->qh, cdata->td_first, transfer, actual);
++        grub_free (cdata);
 -      grub_cpu_idle ();
++        return err;
+       }
 -  grub_dprintf ("uhci", "transaction complete\n");
+     }
 - fail:
++  /* Fall through, no errors occurred, so the QH might be
++     updated.  */
++  grub_dprintf ("uhci", "transaction fallthrough\n");
 -  if (err != GRUB_USB_ERR_NONE)
 -    grub_dprintf ("uhci", "transaction failed\n");
++  return GRUB_USB_ERR_WAIT;
++}
 -  qh->elinkptr = 1;
 -  grub_free_queue (u, td_first, transfer, actual);
++static grub_usb_err_t
++grub_uhci_cancel_transfer (grub_usb_controller_t dev,
++                         grub_usb_transfer_t transfer)
++{
++  struct grub_uhci *u = (struct grub_uhci *) dev->data;
++  grub_size_t actual;
++  struct grub_uhci_transfer_controller_data *cdata = transfer->controller_data;
++
++  grub_dprintf ("uhci", "transaction cancel\n");
+   /* Place the QH back in the free list and deallocate the associated
+      TDs.  */
 -  return err;
++  cdata->qh->elinkptr = 1;
++  grub_free_queue (u, cdata->qh, cdata->td_first, transfer, &actual);
++  grub_free (cdata);
 -  grub_uhci_writereg16 (u, reg, status | (1 << 1));
++  return GRUB_USB_ERR_NONE;
+ }
+ static int
+ grub_uhci_iterate (int (*hook) (grub_usb_controller_t dev))
+ {
+   struct grub_uhci *u;
+   struct grub_usb_controller dev;
+   for (u = uhci; u; u = u->next)
+     {
+       dev.data = u;
+       if (hook (&dev))
+       return 1;
+     }
+   return 0;
+ }
+ static grub_err_t
+ grub_uhci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable)
+ {
+   struct grub_uhci *u = (struct grub_uhci *) dev->data;
+   int reg;
+   unsigned int status;
+   grub_uint64_t endtime;
+   grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
+   
+   grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
+   if (port == 0)
+     reg = GRUB_UHCI_REG_PORTSC1;
+   else if (port == 1)
+     reg = GRUB_UHCI_REG_PORTSC2;
+   else
+     return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                      "UHCI Root Hub port does not exist");
+   status = grub_uhci_readreg16 (u, reg);
+   grub_dprintf ("uhci", "detect=0x%02x\n", status);
+   if (!enable) /* We don't need reset port */
+     {
+       /* Disable the port.  */
+       grub_uhci_writereg16 (u, reg, 0 << 2);
+       grub_dprintf ("uhci", "waiting for the port to be disabled\n");
+       endtime = grub_get_time_ms () + 1000;
+       while ((grub_uhci_readreg16 (u, reg) & (1 << 2)))
+         if (grub_get_time_ms () > endtime)
+           return grub_error (GRUB_ERR_IO, "UHCI Timed out");
+       status = grub_uhci_readreg16 (u, reg);
+       grub_dprintf ("uhci", ">3detect=0x%02x\n", status);
+       return GRUB_ERR_NONE;
+     }
+     
+   /* Reset the port.  */
+   grub_uhci_writereg16 (u, reg, 1 << 9);
+   /* Wait for the reset to complete.  XXX: How long exactly?  */
+   grub_millisleep (50); /* For root hub should be nominaly 50ms */
+   status = grub_uhci_readreg16 (u, reg);
+   grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
+   grub_dprintf ("uhci", "reset completed\n");
+   grub_millisleep (10);
+   /* Enable the port.  */
+   grub_uhci_writereg16 (u, reg, 1 << 2);
+   grub_millisleep (10);
+   grub_dprintf ("uhci", "waiting for the port to be enabled\n");
+   endtime = grub_get_time_ms () + 1000;
+   while (! ((status = grub_uhci_readreg16 (u, reg)) & (1 << 2)))
+     if (grub_get_time_ms () > endtime)
+       return grub_error (GRUB_ERR_IO, "UHCI Timed out");
+   /* Reset bit Connect Status Change */
 -  *changed = ((status & (1 << 1)) != 0);
++  grub_uhci_writereg16 (u, reg, status | GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED);
+   /* Read final port status */
+   status = grub_uhci_readreg16 (u, reg);
+   grub_dprintf ("uhci", ">3detect=0x%02x\n", status);
+   return GRUB_ERR_NONE;
+ }
+ static grub_usb_speed_t
+ grub_uhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
+ {
+   struct grub_uhci *u = (struct grub_uhci *) dev->data;
+   int reg;
+   unsigned int status;
+   grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
+   
+   if (port == 0)
+     reg = GRUB_UHCI_REG_PORTSC1;
+   else if (port == 1)
+     reg = GRUB_UHCI_REG_PORTSC2;
+   else
+     return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                      "UHCI Root Hub port does not exist");
+   status = grub_uhci_readreg16 (u, reg);
+   grub_dprintf ("uhci", "detect=0x%02x port=%d\n", status, port);
+   /* Connect Status Change bit - it detects change of connection */
 -  .transfer = grub_uhci_transfer,
++  if (status & (1 << 1))
++    {
++      *changed = 1;
++      /* Reset bit Connect Status Change */
++      grub_uhci_writereg16 (u, reg, (status & GRUB_UHCI_REG_PORTSC_RW)
++                          | GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED);
++    }
++  else
++    *changed = 0;
+     
+   if (! (status & 1))
+     return GRUB_USB_SPEED_NONE;
+   else if (status & (1 << 8))
+     return GRUB_USB_SPEED_LOW;
+   else
+     return GRUB_USB_SPEED_FULL;
+ }
+ static int
+ grub_uhci_hubports (grub_usb_controller_t dev __attribute__((unused)))
+ {
+   /* The root hub has exactly two ports.  */
+   return 2;
+ }
\f
+ static struct grub_usb_controller_dev usb_controller =
+ {
+   .name = "uhci",
+   .iterate = grub_uhci_iterate,
++  .setup_transfer = grub_uhci_setup_transfer,
++  .check_transfer = grub_uhci_check_transfer,
++  .cancel_transfer = grub_uhci_cancel_transfer,
+   .hubports = grub_uhci_hubports,
+   .portstatus = grub_uhci_portstatus,
+   .detect_dev = grub_uhci_detect_dev
+ };
+ GRUB_MOD_INIT(uhci)
+ {
+   grub_uhci_inithw ();
+   grub_usb_controller_dev_register (&usb_controller);
+   grub_dprintf ("uhci", "registered\n");
+ }
+ GRUB_MOD_FINI(uhci)
+ {
+   struct grub_uhci *u;
+   /* Disable all UHCI controllers.  */
+   for (u = uhci; u; u = u->next)
+     grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 0);
+   /* Unregister the controller.  */
+   grub_usb_controller_dev_unregister (&usb_controller);
+ }
index 0000000000000000000000000000000000000000,7f2c8d24bc1e78a6e4356cbc4657079d2b0bb02a..0ddba32559c3c8fca3431b5067bf5659346b9e12
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,521 +1,479 @@@
 -  grub_uint64_t timeout;
 -  grub_usb_device_t next_dev;
 -  grub_usb_device_t *attached_devices;
+ /* usb.c - USB Hub Support.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/mm.h>
+ #include <grub/usb.h>
+ #include <grub/misc.h>
+ #include <grub/time.h>
+ #define GRUB_USBHUB_MAX_DEVICES 128
+ /* USB Supports 127 devices, with device 0 as special case.  */
+ static struct grub_usb_device *grub_usb_devs[GRUB_USBHUB_MAX_DEVICES];
++static int rescan = 0;
++
+ struct grub_usb_hub
+ {
+   struct grub_usb_hub *next;
+   grub_usb_controller_t controller;
+   int nports;
+   struct grub_usb_device **devices;
+   grub_usb_device_t dev;
+ };
+ struct grub_usb_hub *hubs;
+ /* Add a device that currently has device number 0 and resides on
+    CONTROLLER, the Hub reported that the device speed is SPEED.  */
+ static grub_usb_device_t
+ grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed)
+ {
+   grub_usb_device_t dev;
+   int i;
+   grub_usb_err_t err;
+   dev = grub_zalloc (sizeof (struct grub_usb_device));
+   if (! dev)
+     return NULL;
+   dev->controller = *controller;
+   dev->speed = speed;
+   err = grub_usb_device_initialize (dev);
+   if (err)
+     {
+       grub_free (dev);
+       return NULL;
+     }
+   /* Assign a new address to the device.  */
+   for (i = 1; i < GRUB_USBHUB_MAX_DEVICES; i++)
+     {
+       if (! grub_usb_devs[i])
+       break;
+     }
+   if (i == GRUB_USBHUB_MAX_DEVICES)
+     {
+       grub_error (GRUB_ERR_IO, "can't assign address to USB device");
+       for (i = 0; i < 8; i++)
+         grub_free (dev->config[i].descconf);
+       grub_free (dev);
+       return NULL;
+     }
+   err = grub_usb_control_msg (dev,
+                             (GRUB_USB_REQTYPE_OUT
+                              | GRUB_USB_REQTYPE_STANDARD
+                              | GRUB_USB_REQTYPE_TARGET_DEV),
+                             GRUB_USB_REQ_SET_ADDRESS,
+                             i, 0, 0, NULL);
+   if (err)
+     {
+       for (i = 0; i < 8; i++)
+         grub_free (dev->config[i].descconf);
+       grub_free (dev);
+       return NULL;
+     }
+   dev->addr = i;
+   dev->initialized = 1;
+   grub_usb_devs[i] = dev;
+   /* Wait "recovery interval", spec. says 2ms */
+   grub_millisleep (2);
+   
+   grub_usb_device_attach (dev);
+   
+   return dev;
+ }
\f
+ static grub_usb_err_t
+ grub_usb_add_hub (grub_usb_device_t dev)
+ {
+   struct grub_usb_usb_hubdesc hubdesc;
+   grub_err_t err;
+   int i;
 -  attached_devices = grub_zalloc (hubdesc.portcnt
 -                                * sizeof (attached_devices[0]));
 -  if (!attached_devices)
+   
+   err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+                                   | GRUB_USB_REQTYPE_CLASS
+                                   | GRUB_USB_REQTYPE_TARGET_DEV),
+                               GRUB_USB_REQ_GET_DESCRIPTOR,
+                             (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
+                             0, sizeof (hubdesc), (char *) &hubdesc);
+   if (err)
+     return err;
+   grub_dprintf ("usb", "Hub descriptor:\n\t\t len:%d, typ:0x%02x, cnt:%d, char:0x%02x, pwg:%d, curr:%d\n",
+                 hubdesc.length, hubdesc.type, hubdesc.portcnt,
+                 hubdesc.characteristics, hubdesc.pwdgood,
+                 hubdesc.current);
+   /* Activate the first configuration. Hubs should have only one conf. */
+   grub_dprintf ("usb", "Hub set configuration\n");
+   grub_usb_set_configuration (dev, 1);
 -  dev->children = attached_devices;
++  dev->children = grub_zalloc (hubdesc.portcnt * sizeof (dev->children[0]));
++  if (!dev->children)
+     return GRUB_USB_ERR_INTERNAL;
 -      err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 -                                      | GRUB_USB_REQTYPE_CLASS
 -                                      | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                GRUB_USB_REQ_SET_FEATURE,
 -                                GRUB_USB_HUB_FEATURE_PORT_POWER,
 -                                i, 0, NULL);
 -      /* Just ignore the device if some error happened */
 -      if (err)
 -      continue;
+   dev->nports = hubdesc.portcnt;
+   /* Power on all Hub ports.  */
+   for (i = 1; i <= hubdesc.portcnt; i++)
+     {
+       grub_dprintf ("usb", "Power on - port %d\n", i);
+       /* Power on the port and wait for possible device connect */
 -  /* Wait for port power-on */
 -  if (hubdesc.pwdgood >= 50)
 -    grub_millisleep (hubdesc.pwdgood * 2);
 -  else
 -    grub_millisleep (100);
 -    
 -  /* Iterate over the Hub ports.  */
 -  for (i = 1; i <= hubdesc.portcnt; i++)
 -    {
 -      grub_uint32_t status;
++      grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                | GRUB_USB_REQTYPE_CLASS
++                                | GRUB_USB_REQTYPE_TARGET_OTHER),
++                          GRUB_USB_REQ_SET_FEATURE,
++                          GRUB_USB_HUB_FEATURE_PORT_POWER,
++                          i, 0, NULL);
+     }
 -      /* Get the port status.  */
 -      err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 -                                      | GRUB_USB_REQTYPE_CLASS
 -                                      | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                GRUB_USB_REQ_GET_STATUS,
 -                                0, i, sizeof (status), (char *) &status);
 -      /* Just ignore the device if the Hub does not report the
 -       status.  */
 -      if (err)
 -      continue;
 -      grub_dprintf ("usb", "Hub port %d status: 0x%02x\n", i, status);
 -      /* If connected, reset and enable the port.  */
 -      if (status & GRUB_USB_HUB_STATUS_CONNECTED)
++  /* Rest will be done on next usb poll.  */
++  for (i = 0; i < dev->config[0].interf[0].descif->endpointcnt;
++       i++)
++    {
++      struct grub_usb_desc_endp *endp = NULL;
++      endp = &dev->config[0].interf[0].descendp[i];
 -        grub_usb_speed_t speed;
 -
 -        /* Determine the device speed.  */
 -        if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
 -          speed = GRUB_USB_SPEED_LOW;
 -        else
 -          {
 -            if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
 -              speed = GRUB_USB_SPEED_HIGH;
 -            else
 -              speed = GRUB_USB_SPEED_FULL;
 -          }
 -
 -        /* A device is actually connected to this port.
 -         * Now do reset of port. */
 -          grub_dprintf ("usb", "Reset hub port - port %d\n", i);
 -        err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 -                                          | GRUB_USB_REQTYPE_CLASS
 -                                          | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                    GRUB_USB_REQ_SET_FEATURE,
 -                                    GRUB_USB_HUB_FEATURE_PORT_RESET,
 -                                    i, 0, 0);
 -        /* If the Hub does not cooperate for this port, just skip
 -           the port.  */
 -        if (err)
 -          continue;
 -
 -          /* Wait for reset procedure done */
 -          timeout = grub_get_time_ms () + 1000;
 -          do
 -            {
 -              /* Get the port status.  */
 -              err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 -                                              | GRUB_USB_REQTYPE_CLASS
 -                                              | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                        GRUB_USB_REQ_GET_STATUS,
 -                                        0, i, sizeof (status), (char *) &status);
 -            }
 -          while (!err &&
 -                 !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) &&
 -                 (grub_get_time_ms() < timeout) );
 -          if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) )
 -            continue;
 -
 -          /* Wait a recovery time after reset, spec. says 10ms */
 -          grub_millisleep (10);
 -   
 -          /* Do reset of connection change bit */
 -          err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 -                                            | GRUB_USB_REQTYPE_CLASS
 -                                            | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                      GRUB_USB_REQ_CLEAR_FEATURE,
 -                                    GRUB_USB_HUB_FEATURE_C_CONNECTED,
 -                                    i, 0, 0);
 -          /* Just ignore the device if the Hub reports some error */
 -          if (err)
 -           continue;
 -          grub_dprintf ("usb", "Hub port - cleared connection change\n");
 -
 -        /* Add the device and assign a device address to it.  */
 -          grub_dprintf ("usb", "Call hub_add_dev - port %d\n", i);
 -        next_dev = grub_usb_hub_add_dev (&dev->controller, speed);
 -          if (! next_dev)
 -            continue;
 -
 -        attached_devices[i - 1] = next_dev;
 -
 -          /* If the device is a Hub, scan it for more devices.  */
 -          if (next_dev->descdev.class == 0x09)
 -            grub_usb_add_hub (next_dev);
++      if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
++        == GRUB_USB_EP_INTERRUPT)
+       {
 -  grub_uint64_t timeout;
 -  grub_usb_device_t next_dev;
 -  grub_usb_device_t *attached_devices = dev->children;
 -      
++        dev->hub_endpoint = endp;
++        dev->hub_transfer
++          = grub_usb_bulk_read_background (dev, endp->endp_addr,
++                                           grub_min (endp->maxpacket,
++                                                     sizeof (dev->statuschange)),
++                                           (char *) &dev->statuschange);
++        break;
+       }
+     }
++  rescan = 1;
++
+   return GRUB_ERR_NONE;
+ }
+ static void
+ attach_root_port (struct grub_usb_hub *hub, int portno,
+                 grub_usb_speed_t speed)
+ {
+   grub_usb_device_t dev;
+   grub_err_t err;
+   /* Disable the port. XXX: Why? */
+   err = hub->controller->dev->portstatus (hub->controller, portno, 0);
+   if (err)
+     return;
+   /* Enable the port.  */
+   err = hub->controller->dev->portstatus (hub->controller, portno, 1);
+   if (err)
+     return;
+   /* Enable the port and create a device.  */
+   dev = grub_usb_hub_add_dev (hub->controller, speed);
+   if (! dev)
+     return;
+   hub->devices[portno] = dev;
+   /* If the device is a Hub, scan it for more devices.  */
+   if (dev->descdev.class == 0x09)
+     grub_usb_add_hub (dev);
+ }
+ grub_usb_err_t
+ grub_usb_root_hub (grub_usb_controller_t controller)
+ {
+   int i;
+   struct grub_usb_hub *hub;
+   int changed=0;
+   hub = grub_malloc (sizeof (*hub));
+   if (!hub)
+     return GRUB_USB_ERR_INTERNAL;
+   hub->next = hubs;
+   hubs = hub;
+   hub->controller = grub_malloc (sizeof (*controller));
+   if (!hub->controller)
+     {
+       grub_free (hub);
+       return GRUB_USB_ERR_INTERNAL;
+     }
+   grub_memcpy (hub->controller, controller, sizeof (*controller));
+   hub->dev = 0;
+   /* Query the number of ports the root Hub has.  */
+   hub->nports = controller->dev->hubports (controller);
+   hub->devices = grub_zalloc (sizeof (hub->devices[0]) * hub->nports);
+   if (!hub->devices)
+     {
+       grub_free (hub->controller);
+       grub_free (hub);
+       return GRUB_USB_ERR_INTERNAL;
+     }
+   for (i = 0; i < hub->nports; i++)
+     {
+       grub_usb_speed_t speed;
+       speed = controller->dev->detect_dev (hub->controller, i,
+                                          &changed);
+       if (speed != GRUB_USB_SPEED_NONE)
+       attach_root_port (hub, i, speed);
+     }
+   return GRUB_USB_ERR_NONE;
+ }
+ static void detach_device (grub_usb_device_t dev);
+ static void
+ detach_device (grub_usb_device_t dev)
+ {
+   unsigned i;
+   int k;
+   if (!dev)
+     return;
+   if (dev->descdev.class == GRUB_USB_CLASS_HUB)
+     {
++      if (dev->hub_transfer)
++      grub_usb_cancel_transfer (dev->hub_transfer);
++
+       for (i = 0; i < dev->nports; i++)
+       detach_device (dev->children[i]);
+       grub_free (dev->children);
+     }
+   for (i = 0; i < ARRAY_SIZE (dev->config); i++)
+     if (dev->config[i].descconf)
+       for (k = 0; k < dev->config[i].descconf->numif; k++)
+       {
+         struct grub_usb_interface *inter = &dev->config[i].interf[k];
+         if (inter && inter->detach_hook)
+           inter->detach_hook (dev, i, k);
+       }
+   grub_usb_devs[dev->addr] = 0;
+ }
+ static void
+ poll_nonroot_hub (grub_usb_device_t dev)
+ {
+   grub_err_t err;
+   unsigned i;
 -      /* Just ignore the device if the Hub does not report the
 -       status.  */
++  grub_uint8_t changed;
++  grub_size_t actual;
++
++  if (!dev->hub_transfer)
++    return;
++
++  err = grub_usb_check_transfer (dev->hub_transfer, &actual);
++
++  if (err == GRUB_USB_ERR_WAIT)
++    return;
++
++  changed = dev->statuschange;
++
++  dev->hub_transfer
++    = grub_usb_bulk_read_background (dev, dev->hub_endpoint->endp_addr,
++                                   grub_min (dev->hub_endpoint->maxpacket,
++                                             sizeof (dev->statuschange)),
++                                   (char *) &dev->statuschange);
++
++  if (err || actual == 0 || changed == 0)
++    return;
++
+   /* Iterate over the Hub ports.  */
+   for (i = 1; i <= dev->nports; i++)
+     {
+       grub_uint32_t status;
++      if (!(changed & (1 << i)))
++      continue;
++
+       /* Get the port status.  */
+       err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+                                       | GRUB_USB_REQTYPE_CLASS
+                                       | GRUB_USB_REQTYPE_TARGET_OTHER),
+                                 GRUB_USB_REQ_GET_STATUS,
+                                 0, i, sizeof (status), (char *) &status);
 -      if (status & GRUB_USB_HUB_STATUS_C_CONNECTED)
++
++      grub_printf ("i = %d, status = %08x\n", i, status);
++
+       if (err)
+       continue;
 -        detach_device (attached_devices[i-1]);
 -        attached_devices[i - 1] = NULL;
 -      }
++      /* FIXME: properly handle these conditions.  */
++      if (status & GRUB_USB_HUB_STATUS_C_PORT_ENABLED)
++      grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                  | GRUB_USB_REQTYPE_CLASS
++                                  | GRUB_USB_REQTYPE_TARGET_OTHER),
++                            GRUB_USB_REQ_CLEAR_FEATURE,
++                            GRUB_USB_HUB_FEATURE_C_PORT_ENABLED, i, 0, 0);
++
++      if (status & GRUB_USB_HUB_STATUS_C_PORT_SUSPEND)
++      grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                  | GRUB_USB_REQTYPE_CLASS
++                                  | GRUB_USB_REQTYPE_TARGET_OTHER),
++                            GRUB_USB_REQ_CLEAR_FEATURE,
++                            GRUB_USB_HUB_FEATURE_C_PORT_SUSPEND, i, 0, 0);
++
++      if (status & GRUB_USB_HUB_STATUS_C_PORT_OVERCURRENT)
++      grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                  | GRUB_USB_REQTYPE_CLASS
++                                  | GRUB_USB_REQTYPE_TARGET_OTHER),
++                            GRUB_USB_REQ_CLEAR_FEATURE,
++                            GRUB_USB_HUB_FEATURE_C_PORT_OVERCURRENT, i, 0, 0);
++
++      if (status & GRUB_USB_HUB_STATUS_C_PORT_CONNECTED)
+       {
 -      /* Connected and status of connection changed ? */
 -      if ((status & GRUB_USB_HUB_STATUS_CONNECTED)
 -          && (status & GRUB_USB_HUB_STATUS_C_CONNECTED))
++        grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                    | GRUB_USB_REQTYPE_CLASS
++                                    | GRUB_USB_REQTYPE_TARGET_OTHER),
++                              GRUB_USB_REQ_CLEAR_FEATURE,
++                              GRUB_USB_HUB_FEATURE_C_PORT_CONNECTED, i, 0, 0);
++
++        detach_device (dev->children[i - 1]);
++        dev->children[i - 1] = NULL;
+                   
 -        grub_usb_speed_t speed;
++        /* Connected and status of connection changed ? */
++        if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
++          {
++            /* A device is actually connected to this port.
++             * Now do reset of port. */
++            grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                        | GRUB_USB_REQTYPE_CLASS
++                                        | GRUB_USB_REQTYPE_TARGET_OTHER),
++                                  GRUB_USB_REQ_SET_FEATURE,
++                                  GRUB_USB_HUB_FEATURE_PORT_RESET,
++                                  i, 0, 0);
++            rescan = 1;
++          }
++      }
++
++      if (status & GRUB_USB_HUB_STATUS_C_PORT_RESET)
+       {
 -        /* Determine the device speed.  */
 -        if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
 -          speed = GRUB_USB_SPEED_LOW;
 -        else
++        grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
++                                    | GRUB_USB_REQTYPE_CLASS
++                                    | GRUB_USB_REQTYPE_TARGET_OTHER),
++                              GRUB_USB_REQ_CLEAR_FEATURE,
++                              GRUB_USB_HUB_FEATURE_C_PORT_RESET, i, 0, 0);
 -            if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
 -              speed = GRUB_USB_SPEED_HIGH;
++        if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
+           {
 -              speed = GRUB_USB_SPEED_FULL;
++            grub_usb_speed_t speed;
++            grub_usb_device_t next_dev;
++
++            /* Determine the device speed.  */
++            if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
++              speed = GRUB_USB_SPEED_LOW;
+             else
 -
 -        /* A device is actually connected to this port.
 -         * Now do reset of port. */
 -        err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 -                                          | GRUB_USB_REQTYPE_CLASS
 -                                          | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                    GRUB_USB_REQ_SET_FEATURE,
 -                                    GRUB_USB_HUB_FEATURE_PORT_RESET,
 -                                    i, 0, 0);
 -        /* If the Hub does not cooperate for this port, just skip
 -           the port.  */
 -        if (err)
 -          continue;
 -
 -          /* Wait for reset procedure done */
 -          timeout = grub_get_time_ms () + 1000;
 -          do
 -            {
 -              /* Get the port status.  */
 -              err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 -                                              | GRUB_USB_REQTYPE_CLASS
 -                                              | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                        GRUB_USB_REQ_GET_STATUS,
 -                                        0, i, sizeof (status), (char *) &status);
 -            }
 -          while (!err &&
 -                 !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) &&
 -                 (grub_get_time_ms() < timeout) );
 -          if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) )
 -            continue;
 -
 -          /* Wait a recovery time after reset, spec. says 10ms */
 -          grub_millisleep (10);
 -
 -          /* Do reset of connection change bit */
 -          err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
 -                                            | GRUB_USB_REQTYPE_CLASS
 -                                            | GRUB_USB_REQTYPE_TARGET_OTHER),
 -                                      GRUB_USB_REQ_CLEAR_FEATURE,
 -                                    GRUB_USB_HUB_FEATURE_C_CONNECTED,
 -                                    i, 0, 0);
 -          /* Just ignore the device if the Hub reports some error */
 -          if (err)
 -           continue;
 -
 -        /* Add the device and assign a device address to it.  */
 -        next_dev = grub_usb_hub_add_dev (&dev->controller, speed);
 -          if (! next_dev)
 -            continue;
 -
 -        attached_devices[i - 1] = next_dev;
 -
 -          /* If the device is a Hub, scan it for more devices.  */
 -          if (next_dev->descdev.class == 0x09)
 -            grub_usb_add_hub (next_dev);
++              {
++                if (status & GRUB_USB_HUB_STATUS_PORT_HIGHSPEED)
++                  speed = GRUB_USB_SPEED_HIGH;
++                else
++                  speed = GRUB_USB_SPEED_FULL;
++              }
++
++            /* Wait a recovery time after reset, spec. says 10ms */
++            grub_millisleep (10);
++
++            /* Add the device and assign a device address to it.  */
++            next_dev = grub_usb_hub_add_dev (&dev->controller, speed);
++            if (! next_dev)
++              continue;
++
++            dev->children[i - 1] = next_dev;
++
++            /* If the device is a Hub, scan it for more devices.  */
++            if (next_dev->descdev.class == 0x09)
++              grub_usb_add_hub (next_dev);
+           }
 -
 -  return;
+       }
+     }
 -  /* We should check changes of non-root hubs too. */
 -  for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
+ }
+ void
+ grub_usb_poll_devices (void)
+ {
+   struct grub_usb_hub *hub;
+   int i;
+   for (hub = hubs; hub; hub = hub->next)
+     {
+       /* Do we have to recheck number of ports?  */
+       /* No, it should be never changed, it should be constant. */
+       for (i = 0; i < hub->nports; i++)
+       {
+         grub_usb_speed_t speed;
+         int changed = 0;
+         speed = hub->controller->dev->detect_dev (hub->controller, i,
+                                                   &changed);
+         if (changed)
+           {
+             detach_device (hub->devices[i]);
+             hub->devices[i] = NULL;
+             if (speed != GRUB_USB_SPEED_NONE)
+                 attach_root_port (hub, i, speed);
+           }
+       }
+     }
 -      grub_usb_device_t dev = grub_usb_devs[i];
 -
 -      if (dev && dev->descdev.class == 0x09)
 -      poll_nonroot_hub (dev);
++  while (1)
+     {
++      rescan = 0;
++      
++      /* We should check changes of non-root hubs too. */
++      for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
++      {
++        grub_usb_device_t dev = grub_usb_devs[i];
++        
++        if (dev && dev->descdev.class == 0x09)
++          poll_nonroot_hub (dev);
++      }
++      if (!rescan)
++      break;
++      grub_millisleep (50);
+     }
+ }
+ int
+ grub_usb_iterate (int (*hook) (grub_usb_device_t dev))
+ {
+   int i;
+   for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
+     {
+       if (grub_usb_devs[i])
+       {
+         if (hook (grub_usb_devs[i]))
+             return 1;
+       }
+     }
+   return 0;
+ }
index 0000000000000000000000000000000000000000,db2ec097a896db44f5edd6cdcfa907b81b07162f..fe55114c764c28c15ffb087a0901bb02f978a55f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,307 +1,410 @@@
 -  err = dev->controller.dev->transfer (&dev->controller, transfer,
 -                                     1000, &actual);
+ /* usbtrans.c - USB Transfers and Transactions.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/pci.h>
+ #include <grub/mm.h>
+ #include <grub/misc.h>
+ #include <grub/usb.h>
+ #include <grub/usbtrans.h>
++#include <grub/time.h>
++
++static grub_usb_err_t
++grub_usb_execute_and_wait_transfer (grub_usb_device_t dev, 
++                                  grub_usb_transfer_t transfer,
++                                  int timeout, grub_size_t *actual)
++{
++  grub_usb_err_t err;
++  grub_uint64_t endtime;
++
++  endtime = grub_get_time_ms () + timeout;
++  err = dev->controller.dev->setup_transfer (&dev->controller, transfer);
++  if (err)
++    return err;
++  while (1)
++    {
++      err = dev->controller.dev->check_transfer (&dev->controller, transfer,
++                                               actual);
++      if (!err)
++      return GRUB_USB_ERR_NONE;
++      if (err != GRUB_USB_ERR_WAIT)
++      return err;
++      if (grub_get_time_ms () > endtime)
++      {
++        err = dev->controller.dev->cancel_transfer (&dev->controller,
++                                                    transfer);
++        if (err)
++          return err;
++        return GRUB_USB_ERR_TIMEOUT;
++      }
++      grub_cpu_idle ();
++    }
++}
+ grub_usb_err_t
+ grub_usb_control_msg (grub_usb_device_t dev,
+                     grub_uint8_t reqtype,
+                     grub_uint8_t request,
+                     grub_uint16_t value,
+                     grub_uint16_t index,
+                     grub_size_t size0, char *data_in)
+ {
+   int i;
+   grub_usb_transfer_t transfer;
+   int datablocks;
+   volatile struct grub_usb_packet_setup *setupdata;
+   grub_uint32_t setupdata_addr;
+   grub_usb_err_t err;
+   unsigned int max;
+   struct grub_pci_dma_chunk *data_chunk, *setupdata_chunk;
+   volatile char *data;
+   grub_uint32_t data_addr;
+   grub_size_t size = size0;
+   grub_size_t actual;
+   /* FIXME: avoid allocation any kind of buffer in a first place.  */
+   data_chunk = grub_memalign_dma32 (128, size ? : 16);
+   if (!data_chunk)
+     return GRUB_USB_ERR_INTERNAL;
+   data = grub_dma_get_virt (data_chunk);
+   data_addr = grub_dma_get_phys (data_chunk);
+   grub_memcpy ((char *) data, data_in, size);
+   grub_dprintf ("usb",
+               "control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x size=%d\n",
+               reqtype, request,  value, index, size);
+   /* Create a transfer.  */
+   transfer = grub_malloc (sizeof (*transfer));
+   if (! transfer)
+     {
+       grub_dma_free (data_chunk);
+       return grub_errno;
+     }
+   setupdata_chunk = grub_memalign_dma32 (32, sizeof (*setupdata));
+   if (! setupdata_chunk)
+     {
+       grub_free (transfer);
+       grub_dma_free (data_chunk);
+       return grub_errno;
+     }
+   setupdata = grub_dma_get_virt (setupdata_chunk);
+   setupdata_addr = grub_dma_get_phys (setupdata_chunk);
+   /* Determine the maximum packet size.  */
+   if (dev->descdev.maxsize0)
+     max = dev->descdev.maxsize0;
+   else
+     max = 64;
+   grub_dprintf ("usb", "control: transfer = %p, dev = %p\n", transfer, dev);
+   datablocks = (size + max - 1) / max;
+   /* XXX: Discriminate between different types of control
+      messages.  */
+   transfer->transcnt = datablocks + 2;
+   transfer->size = size; /* XXX ? */
+   transfer->endpoint = 0;
+   transfer->devaddr = dev->addr;
+   transfer->type = GRUB_USB_TRANSACTION_TYPE_CONTROL;
+   transfer->max = max;
+   transfer->dev = dev;
+   /* Allocate an array of transfer data structures.  */
+   transfer->transactions = grub_malloc (transfer->transcnt
+                                       * sizeof (struct grub_usb_transfer));
+   if (! transfer->transactions)
+     {
+       grub_free (transfer);
+       grub_dma_free (setupdata_chunk);
+       grub_dma_free (data_chunk);
+       return grub_errno;
+     }
+   /* Build a Setup packet.  XXX: Endianness.  */
+   setupdata->reqtype = reqtype;
+   setupdata->request = request;
+   setupdata->value = value;
+   setupdata->index = index;
+   setupdata->length = size;
+   transfer->transactions[0].size = sizeof (*setupdata);
+   transfer->transactions[0].pid = GRUB_USB_TRANSFER_TYPE_SETUP;
+   transfer->transactions[0].data = setupdata_addr;
+   transfer->transactions[0].toggle = 0;
+   /* Now the data...  XXX: Is this the right way to transfer control
+      transfers?  */
+   for (i = 0; i < datablocks; i++)
+     {
+       grub_usb_transaction_t tr = &transfer->transactions[i + 1];
+       tr->size = (size > max) ? max : size;
+       /* Use the right most bit as the data toggle.  Simple and
+        effective.  */
+       tr->toggle = !(i & 1);
+       if (reqtype & 128)
+       tr->pid = GRUB_USB_TRANSFER_TYPE_IN;
+       else
+       tr->pid = GRUB_USB_TRANSFER_TYPE_OUT;
+       tr->data = data_addr + i * max;
+       tr->preceding = i * max;
+       size -= max;
+     }
+   /* End with an empty OUT transaction.  */
+   transfer->transactions[datablocks + 1].size = 0;
+   transfer->transactions[datablocks + 1].data = 0;
+   if ((reqtype & 128) && datablocks)
+     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
+   else
+     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
+   transfer->transactions[datablocks + 1].toggle = 1;
 -static grub_usb_err_t
 -grub_usb_bulk_readwrite (grub_usb_device_t dev,
 -                       int endpoint, grub_size_t size0, char *data_in,
 -                       grub_transfer_type_t type, int timeout,
 -                       grub_size_t *actual)
++  err = grub_usb_execute_and_wait_transfer (dev, transfer, 1000, &actual);
++
+   grub_dprintf ("usb", "control: err=%d\n", err);
+   grub_free (transfer->transactions);
+   
+   grub_free (transfer);
+   grub_dma_free (data_chunk);
+   grub_dma_free (setupdata_chunk);
+   grub_memcpy (data_in, (char *) data, size0);
+   return err;
+ }
 -  grub_usb_err_t err;
 -  int toggle = dev->toggle[endpoint];
++static grub_usb_transfer_t
++grub_usb_bulk_setup_readwrite (grub_usb_device_t dev,
++                             int endpoint, grub_size_t size0, char *data_in,
++                             grub_transfer_type_t type)
+ {
+   int i;
+   grub_usb_transfer_t transfer;
+   int datablocks;
+   unsigned int max;
 -    return GRUB_USB_ERR_INTERNAL;
+   volatile char *data;
+   grub_uint32_t data_addr;
+   struct grub_pci_dma_chunk *data_chunk;
+   grub_size_t size = size0;
++  int toggle = dev->toggle[endpoint];
+   grub_dprintf ("usb", "bulk: size=0x%02x type=%d\n", size, type);
+   /* FIXME: avoid allocation any kind of buffer in a first place.  */
+   data_chunk = grub_memalign_dma32 (128, size);
+   if (!data_chunk)
 -      return grub_errno;
++    return NULL;
+   data = grub_dma_get_virt (data_chunk);
+   data_addr = grub_dma_get_phys (data_chunk);
+   if (type == GRUB_USB_TRANSFER_TYPE_OUT)
+     grub_memcpy ((char *) data, data_in, size);
+   /* Use the maximum packet size given in the endpoint descriptor.  */
+   if (dev->initialized)
+     {
+       struct grub_usb_desc_endp *endpdesc;
+       endpdesc = grub_usb_get_endpdescriptor (dev, 0);
+       if (endpdesc)
+       max = endpdesc->maxpacket;
+       else
+       max = 64;
+     }
+   else
+     max = 64;
+   /* Create a transfer.  */
+   transfer = grub_malloc (sizeof (struct grub_usb_transfer));
+   if (! transfer)
+     {
+       grub_dma_free (data_chunk);
 -  transfer->endpoint = endpoint & 15;
++      return NULL;
+     }
+   datablocks = ((size + max - 1) / max);
+   transfer->transcnt = datablocks;
+   transfer->size = size - 1;
 -      return grub_errno;
++  transfer->endpoint = endpoint;
+   transfer->devaddr = dev->addr;
+   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
++  transfer->dir = type;
+   transfer->max = max;
+   transfer->dev = dev;
+   transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */
++  transfer->data_chunk = data_chunk;
++  transfer->data = data_in;
+   /* Allocate an array of transfer data structures.  */
+   transfer->transactions = grub_malloc (transfer->transcnt
+                                       * sizeof (struct grub_usb_transfer));
+   if (! transfer->transactions)
+     {
+       grub_free (transfer);
+       grub_dma_free (data_chunk);
 -  err = dev->controller.dev->transfer (&dev->controller, transfer, timeout,
 -                                     actual);
++      return NULL;
+     }
+   /* Set up all transfers.  */
+   for (i = 0; i < datablocks; i++)
+     {
+       grub_usb_transaction_t tr = &transfer->transactions[i];
+       tr->size = (size > max) ? max : size;
+       /* XXX: Use the right most bit as the data toggle.  Simple and
+        effective.  */
+       tr->toggle = toggle;
+       toggle = toggle ? 0 : 1;
+       tr->pid = type;
+       tr->data = data_addr + i * max;
+       tr->preceding = i * max;
+       size -= tr->size;
+     }
++  return transfer;
++}
++
++static void
++grub_usb_bulk_finish_readwrite (grub_usb_transfer_t transfer)
++{
++  grub_usb_device_t dev = transfer->dev;
++  int toggle = dev->toggle[transfer->endpoint];
 -    toggle = dev->toggle[endpoint]; /* Nothing done, take original */
 -  grub_dprintf ("usb", "bulk: err=%d, toggle=%d\n", err, toggle);
 -  dev->toggle[endpoint] = toggle;
+   /* We must remember proper toggle value even if some transactions
+    * were not processed - correct value should be inversion of last
+    * processed transaction (TD). */
+   if (transfer->last_trans >= 0)
+     toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
+   else
 -  grub_dma_free (data_chunk);
++    toggle = dev->toggle[transfer->endpoint]; /* Nothing done, take original */
++  grub_dprintf ("usb", "bulk: toggle=%d\n", toggle);
++  dev->toggle[transfer->endpoint] = toggle;
++
++  if (transfer->dir == GRUB_USB_TRANSFER_TYPE_IN)
++    grub_memcpy (transfer->data, (void *)
++               grub_dma_get_virt (transfer->data_chunk),
++               transfer->size + 1);
+   grub_free (transfer->transactions);
+   grub_free (transfer);
 -  if (type == GRUB_USB_TRANSFER_TYPE_IN)
 -    grub_memcpy (data_in, (char *) data, size0);
++  grub_dma_free (transfer->data_chunk);
++}
++static grub_usb_err_t
++grub_usb_bulk_readwrite (grub_usb_device_t dev,
++                       int endpoint, grub_size_t size0, char *data_in,
++                       grub_transfer_type_t type, int timeout,
++                       grub_size_t *actual)
++{
++  grub_usb_err_t err;
++  grub_usb_transfer_t transfer;
++
++  transfer = grub_usb_bulk_setup_readwrite (dev, endpoint, size0,
++                                          data_in, type);
++  if (!transfer)
++    return GRUB_USB_ERR_INTERNAL;
++  err = grub_usb_execute_and_wait_transfer (dev, transfer, timeout, actual);
++
++  grub_usb_bulk_finish_readwrite (transfer);
+   return err;
+ }
+ grub_usb_err_t
+ grub_usb_bulk_write (grub_usb_device_t dev,
+                    int endpoint, grub_size_t size, char *data)
+ {
+   grub_size_t actual;
+   grub_usb_err_t err;
+   err = grub_usb_bulk_readwrite (dev, endpoint, size, data,
+                                GRUB_USB_TRANSFER_TYPE_OUT, 1000, &actual);
+   if (!err && actual != size)
+     err = GRUB_USB_ERR_DATA;
+   return err;
+ }
+ grub_usb_err_t
+ grub_usb_bulk_read (grub_usb_device_t dev,
+                   int endpoint, grub_size_t size, char *data)
+ {
+   grub_size_t actual;
+   grub_usb_err_t err;
+   err = grub_usb_bulk_readwrite (dev, endpoint, size, data,
+                                GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual);
+   if (!err && actual != size)
+     err = GRUB_USB_ERR_DATA;
+   return err;
+ }
++grub_usb_err_t
++grub_usb_check_transfer (grub_usb_transfer_t transfer, grub_size_t *actual)
++{
++  grub_usb_err_t err;
++  grub_usb_device_t dev = transfer->dev;
++
++  err = dev->controller.dev->check_transfer (&dev->controller, transfer,
++                                           actual);
++  if (err == GRUB_USB_ERR_WAIT)
++    return err;
++
++  grub_usb_bulk_finish_readwrite (transfer);
++
++  return err;
++}
++
++grub_usb_transfer_t
++grub_usb_bulk_read_background (grub_usb_device_t dev,
++                             int endpoint, grub_size_t size, void *data)
++{
++  grub_usb_err_t err;
++  grub_usb_transfer_t transfer;
++
++  transfer = grub_usb_bulk_setup_readwrite (dev, endpoint, size,
++                                          data, GRUB_USB_TRANSFER_TYPE_IN);
++  if (!transfer)
++    return NULL;
++
++  err = dev->controller.dev->setup_transfer (&dev->controller, transfer);
++  if (err)
++    return NULL;
++
++  return transfer;
++}
++
++void
++grub_usb_cancel_transfer (grub_usb_transfer_t transfer)
++{
++  grub_usb_device_t dev = transfer->dev;
++  dev->controller.dev->cancel_transfer (&dev->controller, transfer);
++  grub_errno = GRUB_ERR_NONE;
++}
++
+ grub_usb_err_t
+ grub_usb_bulk_read_extended (grub_usb_device_t dev,
+                            int endpoint, grub_size_t size, char *data,
+                            int timeout, grub_size_t *actual)
+ {
+   return grub_usb_bulk_readwrite (dev, endpoint, size, data,
+                                 GRUB_USB_TRANSFER_TYPE_IN, timeout, actual);
+ }
index 0000000000000000000000000000000000000000,77a4cc7937040f276dfcf2943ff35d1ddbeabb6f..07d03e376b34bc5357afb0adee8780d9c6d2feef
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,103 +1,103 @@@
 -           (key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != GRUB_TERM_ESC)
+ /* cat.c - command to show the contents of a file  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2005,2007,2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/file.h>
+ #include <grub/disk.h>
+ #include <grub/term.h>
+ #include <grub/misc.h>
+ #include <grub/gzio.h>
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ static const struct grub_arg_option options[] =
+   {
+     {"dos", -1, 0, N_("Accept DOS-style CR/NL line endings."), 0, 0},
+     {0, 0, 0, 0, 0, 0}
+   };
+ static grub_err_t
+ grub_cmd_cat (grub_extcmd_t cmd, int argc, char **args)
+ {
+   struct grub_arg_list *state = cmd->state;
+   int dos = 0;
+   grub_file_t file;
+   char buf[GRUB_DISK_SECTOR_SIZE];
+   grub_ssize_t size;
+   int key = 0;
+   if (state[0].set)
+     dos = 1;
+   if (argc != 1)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+   file = grub_gzfile_open (args[0], 1);
+   if (! file)
+     return grub_errno;
+   while ((size = grub_file_read (file, buf, sizeof (buf))) > 0
+        && key != GRUB_TERM_ESC)
+     {
+       int i;
+       for (i = 0; i < size; i++)
+       {
+         unsigned char c = buf[i];
+         if ((grub_isprint (c) || grub_isspace (c)) && c != '\r')
+           grub_printf ("%c", c);
+         else if (dos && c == '\r' && i + 1 < size && buf[i + 1] == '\n')
+           {
+             grub_printf ("\n");
+             i++;
+           }
+         else
+           {
+             grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
+             grub_printf ("<%x>", (int) c);
+             grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
+           }
+       }
+       while (grub_checkkey () >= 0 &&
++           (key = grub_getkey ()) != GRUB_TERM_ESC)
+       ;
+     }
+   grub_xputs ("\n");
+   grub_refresh ();
+   grub_file_close (file);
+   return 0;
+ }
+ static grub_extcmd_t cmd;
\f
+ GRUB_MOD_INIT(cat)
+ {
+   cmd = grub_register_extcmd ("cat", grub_cmd_cat, GRUB_COMMAND_FLAG_BOTH,
+                             N_("FILE"), N_("Show the contents of a file."),
+                             options);
+ }
+ GRUB_MOD_FINI(cat)
+ {
+   grub_unregister_extcmd (cmd);
+ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..deb482a850c6f2b81a7b0b7fdb4dc811e2acc7ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,297 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2002,2003,2005,2007,2008,2009  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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/term.h>
++#include <grub/err.h>
++#include <grub/mm.h>
++#include <grub/misc.h>
++#include <grub/env.h>
++#include <grub/time.h>
++#include <grub/dl.h>
++#include <grub/keyboard_layouts.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++#include <grub/file.h>
++
++static struct grub_keyboard_layout layout_us = {
++  .keyboard_map = {
++    /* Keyboard errors. Handled by driver.  */
++    /* 0x00 */   0,   0,   0,   0,
++
++    /* 0x04 */ 'a',  'b',  'c',  'd', 
++    /* 0x08 */ 'e',  'f',  'g',  'h',  'i', 'j', 'k', 'l',
++    /* 0x10 */ 'm',  'n',  'o',  'p',  'q', 'r', 's', 't',
++    /* 0x18 */ 'u',  'v',  'w',  'x',  'y', 'z', '1', '2',
++    /* 0x20 */ '3',  '4',  '5',  '6',  '7', '8', '9', '0',
++    /* 0x28 */ '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
++    /* According to usage table 0x31 should be mapped to '/'
++       but testing with real keyboard shows that 0x32 is remapped to '/'.
++       Map 0x31 to 0. 
++    */
++    /* 0x30 */ ']',   0,   '\\', ';', '\'', '`', ',', '.',
++    /* 0x39 is CapsLock. Handled by driver.  */
++    /* 0x38 */ '/',   0,   GRUB_TERM_KEY_F1, GRUB_TERM_KEY_F2, 
++    /* 0x3c */ GRUB_TERM_KEY_F3,     GRUB_TERM_KEY_F4,
++    /* 0x3e */ GRUB_TERM_KEY_F5,     GRUB_TERM_KEY_F6,
++    /* 0x40 */ GRUB_TERM_KEY_F7,     GRUB_TERM_KEY_F8,
++    /* 0x42 */ GRUB_TERM_KEY_F9,     GRUB_TERM_KEY_F10,
++    /* 0x44 */ GRUB_TERM_KEY_F11,    GRUB_TERM_KEY_F12,
++    /* PrtScr and ScrollLock. Not handled yet.  */
++    /* 0x46 */ 0,                    0,
++    /* 0x48 is Pause. Not handled yet.  */
++    /* 0x48 */ 0,                    GRUB_TERM_KEY_INSERT, 
++    /* 0x4a */ GRUB_TERM_KEY_HOME,   GRUB_TERM_KEY_PPAGE,
++    /* 0x4c */ GRUB_TERM_KEY_DC,     GRUB_TERM_KEY_END,
++    /* 0x4e */ GRUB_TERM_KEY_NPAGE,  GRUB_TERM_KEY_RIGHT,
++    /* 0x50 */ GRUB_TERM_KEY_LEFT,   GRUB_TERM_KEY_DOWN,
++    /* 0x53 is NumLock. Handled by driver.  */
++    /* 0x52 */ GRUB_TERM_KEY_UP,     0,
++    /* 0x54 */ '/',                  '*', 
++    /* 0x56 */ '-',                  '+',
++    /* 0x58 */ '\n',                 GRUB_TERM_KEY_END, 
++    /* 0x5a */ GRUB_TERM_KEY_DOWN,   GRUB_TERM_KEY_NPAGE,
++    /* 0x5c */ GRUB_TERM_KEY_LEFT,   GRUB_TERM_KEY_CENTER,
++    /* 0x5e */ GRUB_TERM_KEY_RIGHT,  GRUB_TERM_KEY_HOME,
++    /* 0x60 */ GRUB_TERM_KEY_UP,     GRUB_TERM_KEY_PPAGE,
++    /* 0x62 */ GRUB_TERM_KEY_INSERT, GRUB_TERM_KEY_DC,
++    /* 0x64 */ '\\'
++  },
++  .keyboard_map_shift = {
++    /* Keyboard errors. Handled by driver.  */
++    /* 0x00 */   0,   0,   0,   0,
++
++    /* 0x04 */ 'A',  'B',  'C',  'D', 
++    /* 0x08 */ 'E',  'F',  'G',  'H',  'I', 'J', 'K', 'L',
++    /* 0x10 */ 'M',  'N',  'O',  'P',  'Q', 'R', 'S', 'T',
++    /* 0x18 */ 'U',  'V',  'W',  'X',  'Y', 'Z', '!', '@',
++    /* 0x20 */ '#',  '$',  '%',  '^',  '&', '*', '(', ')',
++    /* 0x28 */ '\n' | GRUB_TERM_SHIFT, '\e' | GRUB_TERM_SHIFT, 
++    /* 0x2a */ '\b' | GRUB_TERM_SHIFT, '\t' | GRUB_TERM_SHIFT, 
++    /* 0x2c */ ' '  | GRUB_TERM_SHIFT,  '_', '+', '{',
++    /* According to usage table 0x31 should be mapped to '/'
++       but testing with real keyboard shows that 0x32 is remapped to '/'.
++       Map 0x31 to 0. 
++    */
++    /* 0x30 */ '}',  0,    '|',  ':',  '"', '~', '<', '>',
++    /* 0x39 is CapsLock. Handled by driver.  */
++    /* 0x38 */ '?',  0,
++    /* 0x3a */ GRUB_TERM_KEY_F1 | GRUB_TERM_SHIFT,
++    /* 0x3b */ GRUB_TERM_KEY_F2 | GRUB_TERM_SHIFT, 
++    /* 0x3c */ GRUB_TERM_KEY_F3 | GRUB_TERM_SHIFT, 
++    /* 0x3d */ GRUB_TERM_KEY_F4 | GRUB_TERM_SHIFT, 
++    /* 0x3e */ GRUB_TERM_KEY_F5 | GRUB_TERM_SHIFT, 
++    /* 0x3f */ GRUB_TERM_KEY_F6 | GRUB_TERM_SHIFT, 
++    /* 0x40 */ GRUB_TERM_KEY_F7 | GRUB_TERM_SHIFT, 
++    /* 0x41 */ GRUB_TERM_KEY_F8 | GRUB_TERM_SHIFT, 
++    /* 0x42 */ GRUB_TERM_KEY_F9 | GRUB_TERM_SHIFT, 
++    /* 0x43 */ GRUB_TERM_KEY_F10 | GRUB_TERM_SHIFT, 
++    /* 0x44 */ GRUB_TERM_KEY_F11 | GRUB_TERM_SHIFT, 
++    /* 0x45 */ GRUB_TERM_KEY_F12 | GRUB_TERM_SHIFT, 
++    /* PrtScr and ScrollLock. Not handled yet.  */
++    /* 0x46 */ 0,                    0,
++    /* 0x48 is Pause. Not handled yet.  */
++    /* 0x48 */ 0,                    GRUB_TERM_KEY_INSERT | GRUB_TERM_SHIFT, 
++    /* 0x4a */ GRUB_TERM_KEY_HOME | GRUB_TERM_SHIFT,
++    /* 0x4b */ GRUB_TERM_KEY_PPAGE | GRUB_TERM_SHIFT,
++    /* 0x4c */ GRUB_TERM_KEY_DC | GRUB_TERM_SHIFT,
++    /* 0x4d */ GRUB_TERM_KEY_END | GRUB_TERM_SHIFT,
++    /* 0x4e */ GRUB_TERM_KEY_NPAGE | GRUB_TERM_SHIFT,
++    /* 0x4f */ GRUB_TERM_KEY_RIGHT | GRUB_TERM_SHIFT,
++    /* 0x50 */ GRUB_TERM_KEY_LEFT | GRUB_TERM_SHIFT,
++    /* 0x51 */ GRUB_TERM_KEY_DOWN | GRUB_TERM_SHIFT,
++    /* 0x53 is NumLock. Handled by driver.  */
++    /* 0x52 */ GRUB_TERM_KEY_UP | GRUB_TERM_SHIFT,     0,
++    /* 0x54 */ '/',                    '*', 
++    /* 0x56 */ '-',                    '+',
++    /* 0x58 */ '\n' | GRUB_TERM_SHIFT, '1', '2', '3', '4', '5','6', '7',
++    /* 0x60 */ '8', '9', '0', '.', '|'
++  }
++};
++
++static struct grub_keyboard_layout *grub_current_layout = &layout_us;
++
++static int
++map_key_core (int code, int status, int *alt_gr_consumed)
++{
++  *alt_gr_consumed = 0;
++
++  if (status & GRUB_TERM_STATUS_RALT)
++    {
++      if (status & (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT))
++      {
++        if (grub_current_layout->keyboard_map_shift_l3[code])
++          {
++            *alt_gr_consumed = 1;
++            return grub_current_layout->keyboard_map_shift_l3[code];
++          }
++      }
++      else if (grub_current_layout->keyboard_map_l3[code])
++      {
++        *alt_gr_consumed = 1;
++        return grub_current_layout->keyboard_map_l3[code];  
++      }
++    }
++  if (status & (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT))
++    return grub_current_layout->keyboard_map_shift[code];
++  else
++    return grub_current_layout->keyboard_map[code];
++}
++
++unsigned
++grub_term_map_key (grub_keyboard_key_t code, int status)
++{
++  int alt_gr_consumed = 0;
++  int key;
++
++  if (code >= 0x59 && code <= 0x63 && (status & GRUB_TERM_STATUS_NUM))
++    {
++      if (status & (GRUB_TERM_STATUS_RSHIFT | GRUB_TERM_STATUS_LSHIFT))
++      status &= ~(GRUB_TERM_STATUS_RSHIFT | GRUB_TERM_STATUS_LSHIFT);
++      else
++      status |= GRUB_TERM_STATUS_RSHIFT;
++    }
++
++  key = map_key_core (code, status, &alt_gr_consumed);
++  
++  if (key == 0 || key == GRUB_TERM_SHIFT)
++    grub_printf ("Unknown key 0x%x detected\n", code);
++  
++  if (status & GRUB_TERM_STATUS_CAPS)
++    {
++      if ((key >= 'a') && (key <= 'z'))
++      key += 'A' - 'a';
++      else if ((key >= 'A') && (key <= 'Z'))
++      key += 'a' - 'A';
++    }
++  
++  if ((status & GRUB_TERM_STATUS_LALT) || 
++      ((status & GRUB_TERM_STATUS_RALT) && !alt_gr_consumed))
++    key |= GRUB_TERM_ALT;
++  if (status & (GRUB_TERM_STATUS_LCTRL | GRUB_TERM_STATUS_RCTRL))
++    key |= GRUB_TERM_CTRL;
++
++  return key;
++}
++
++static grub_err_t
++grub_cmd_keymap (struct grub_command *cmd __attribute__ ((unused)),
++               int argc, char *argv[])
++{
++  char *filename;
++  grub_file_t file;
++  grub_uint32_t version;
++  grub_uint8_t magic[GRUB_KEYBOARD_LAYOUTS_FILEMAGIC_SIZE];
++  struct grub_keyboard_layout *newmap = NULL;
++  unsigned i;
++
++  if (argc < 1)
++    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file or layout name required");
++  if (argv[0][0] != '(' && argv[0][0] != '/' && argv[0][0] != '+')
++    {
++      const char *prefix = grub_env_get ("prefix");
++      if (!prefix)
++      return grub_error (GRUB_ERR_BAD_ARGUMENT, "No prefix set");     
++      filename = grub_xasprintf ("%s/layouts/%s.gkb", prefix, argv[0]);
++      if (!filename)
++      return grub_errno;
++    }
++  else
++    filename = argv[0];
++
++  file = grub_file_open (filename);
++  if (! file)
++    goto fail;
++
++  if (grub_file_read (file, magic, sizeof (magic)) != sizeof (magic))
++    {
++      if (!grub_errno)
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "file is too short");
++      goto fail;
++    }
++
++  if (grub_memcmp (magic, GRUB_KEYBOARD_LAYOUTS_FILEMAGIC,
++                 GRUB_KEYBOARD_LAYOUTS_FILEMAGIC_SIZE) != 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid magic");
++      goto fail;
++    }
++
++  if (grub_file_read (file, &version, sizeof (version)) != sizeof (version))
++    {
++      if (!grub_errno)
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "file is too short");
++      goto fail;
++    }
++
++  if (grub_le_to_cpu32 (version) != GRUB_KEYBOARD_LAYOUTS_VERSION)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid version");
++      goto fail;
++    }
++
++  newmap = grub_malloc (sizeof (*newmap));
++  if (!newmap)
++    goto fail;
++
++  if (grub_file_read (file, newmap, sizeof (*newmap)) != sizeof (*newmap))
++    {
++      if (!grub_errno)
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "file is too short");
++      goto fail;
++    }
++
++  for (i = 0; i < ARRAY_SIZE (newmap->keyboard_map); i++)
++    newmap->keyboard_map[i] = grub_le_to_cpu32(newmap->keyboard_map[i]);
++
++  for (i = 0; i < ARRAY_SIZE (newmap->keyboard_map_shift); i++)
++    newmap->keyboard_map_shift[i]
++      = grub_le_to_cpu32(newmap->keyboard_map_shift[i]);
++
++  for (i = 0; i < ARRAY_SIZE (newmap->keyboard_map_l3); i++)
++    newmap->keyboard_map_l3[i]
++      = grub_le_to_cpu32(newmap->keyboard_map_l3[i]);
++
++  for (i = 0; i < ARRAY_SIZE (newmap->keyboard_map_shift_l3); i++)
++    newmap->keyboard_map_shift_l3[i]
++      = grub_le_to_cpu32(newmap->keyboard_map_shift_l3[i]);
++
++  grub_current_layout = newmap;
++
++  return GRUB_ERR_NONE;
++
++ fail:
++  if (filename != argv[0])
++    grub_free (filename);
++  grub_free (newmap);
++  if (file)
++    grub_file_close (file);
++  return grub_errno;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(keylayouts)
++{
++  cmd = grub_register_command ("keymap", grub_cmd_keymap,
++                             0, N_("Load a keyboard layout."));
++}
++
++GRUB_MOD_FINI(keylayouts)
++{
++  grub_unregister_command (cmd);
++}
index 0000000000000000000000000000000000000000,838792889ff3a58322f53965c6e9dd35ae5c6e57..7525b346e8967f40cac84b520a3373b452627ff7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,93 +1,109 @@@
 -#define grub_cur_term_input   grub_term_get_current_input ()
+ /* keystatus.c - Command to check key modifier status.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+ #include <grub/extcmd.h>
+ #include <grub/term.h>
+ #include <grub/i18n.h>
+ static const struct grub_arg_option options[] =
+   {
+     {"shift", 's', 0, N_("Check Shift key."), 0, 0},
+     {"ctrl", 'c', 0, N_("Check Control key."), 0, 0},
+     {"alt", 'a', 0, N_("Check Alt key."), 0, 0},
+     {0, 0, 0, 0, 0, 0}
+   };
 -    expect_mods |= GRUB_TERM_STATUS_SHIFT;
++static int
++grub_getkeystatus (void)
++{
++  int status = 0;
++  grub_term_input_t term;
++
++  if (grub_term_poll_usb)
++    grub_term_poll_usb ();
++
++  FOR_ACTIVE_TERM_INPUTS(term)
++  {
++    if (term->getkeystatus)
++      status |= term->getkeystatus (term);
++  }
++
++  return status;
++}
+ static grub_err_t
+ grub_cmd_keystatus (grub_extcmd_t cmd,
+                   int argc __attribute__ ((unused)),
+                   char **args __attribute__ ((unused)))
+ {
+   struct grub_arg_list *state = cmd->state;
+   int expect_mods = 0;
+   int mods;
+   if (state[0].set)
 -    expect_mods |= GRUB_TERM_STATUS_CTRL;
++    expect_mods |= (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT);
+   if (state[1].set)
 -    expect_mods |= GRUB_TERM_STATUS_ALT;
++    expect_mods |= (GRUB_TERM_STATUS_LCTRL | GRUB_TERM_STATUS_RCTRL);
+   if (state[2].set)
++    expect_mods |= (GRUB_TERM_STATUS_LALT | GRUB_TERM_STATUS_RALT);
+   grub_dprintf ("keystatus", "expect_mods: %d\n", expect_mods);
+   /* Without arguments, just check whether getkeystatus is supported at
+      all.  */
+   if (expect_mods == 0)
+     {
+       grub_term_input_t term;
+       int nterms = 0;
+       FOR_ACTIVE_TERM_INPUTS (term)
+       if (!term->getkeystatus)
+         return grub_error (GRUB_ERR_TEST_FAILURE, "false");
+       else
+         nterms++;
+       if (!nterms)
+       return grub_error (GRUB_ERR_TEST_FAILURE, "false");
+       return 0;
+     }
+   mods = grub_getkeystatus ();
+   grub_dprintf ("keystatus", "mods: %d\n", mods);
+   if (mods >= 0 && (mods & expect_mods) != 0)
+     return 0;
+   else
+     return grub_error (GRUB_ERR_TEST_FAILURE, "false");
+ }
+ static grub_extcmd_t cmd;
\f
+ GRUB_MOD_INIT(keystatus)
+ {
+   cmd = grub_register_extcmd ("keystatus", grub_cmd_keystatus,
+                             GRUB_COMMAND_FLAG_BOTH,
+                             N_("[--shift] [--ctrl] [--alt]"),
+                             N_("Check key modifier status."),
+                             options);
+ }
+ GRUB_MOD_FINI(keystatus)
+ {
+   grub_unregister_extcmd (cmd);
+ }
index 0000000000000000000000000000000000000000,ead279506ed82b12a54008e8555944525d9a9884..bce1aee1d023d48798255c660479a39755e79d90
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,113 +1,112 @@@
 -    if (grub_checkkey () >= 0 &&
 -      GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC)
+ /* sleep.c - Command to wait a specified number of seconds.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2006,2007,2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/term.h>
+ #include <grub/time.h>
+ #include <grub/types.h>
+ #include <grub/misc.h>
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ static const struct grub_arg_option options[] =
+   {
+     {"verbose", 'v', 0, N_("Verbose countdown."), 0, 0},
+     {"interruptible", 'i', 0, N_("Interruptible with ESC."), 0, 0},
+     {0, 0, 0, 0, 0, 0}
+   };
+ static grub_uint16_t *pos;
+ static void
+ do_print (int n)
+ {
+   grub_term_restore_pos (pos);
+   /* NOTE: Do not remove the trailing space characters.
+      They are required to clear the line.  */
+   grub_printf ("%d    ", n);
+ }
+ /* Based on grub_millisleep() from kern/generic/millisleep.c.  */
+ static int
+ grub_interruptible_millisleep (grub_uint32_t ms)
+ {
+   grub_uint64_t start;
+   start = grub_get_time_ms ();
+   while (grub_get_time_ms () - start < ms)
++    if (grub_checkkey () >= 0 && grub_getkey () == GRUB_TERM_ESC)
+       return 1;
+   return 0;
+ }
+ static grub_err_t
+ grub_cmd_sleep (grub_extcmd_t cmd, int argc, char **args)
+ {
+   struct grub_arg_list *state = cmd->state;
+   int n;
+   if (argc != 1)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing operand");
+   n = grub_strtoul (args[0], 0, 10);
+   if (n == 0)
+     {
+       /* Either `0' or broken input.  */
+       return 0;
+     }
+   pos = grub_term_save_pos ();
+   for (; n; n--)
+     {
+       if (state[0].set)
+       do_print (n);
+       if (state[1].set)
+       {
+         if (grub_interruptible_millisleep (1000))
+           return 1;
+       }
+       else
+       grub_millisleep (1000);
+     }
+   if (state[0].set)
+     do_print (0);
+   return 0;
+ }
+ static grub_extcmd_t cmd;
\f
+ GRUB_MOD_INIT(sleep)
+ {
+   cmd = grub_register_extcmd ("sleep", grub_cmd_sleep, GRUB_COMMAND_FLAG_BOTH,
+                             N_("NUMBER_OF_SECONDS"),
+                             N_("Wait for a specified number of seconds."),
+                             options);
+ }
+ GRUB_MOD_FINI(sleep)
+ {
+   grub_unregister_extcmd (cmd);
+ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a492dc2f995c7f3dae6a1d7094abf6842fbe81f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++gcc -c -m32 -DELF32 -o efiemu32.o ./efiemu.c -Wall -Werror -nostdlib -O2 -I. -I../../include
++gcc -c -m64 -DELF64 -o efiemu64_c.o ./efiemu.c -Wall -Werror -mcmodel=large -O2 -I. -I../../include
++gcc -c -m64 -DELF64 -o efiemu64_s.o ./efiemu.S -Wall -Werror -mcmodel=large -O2 -I. -I../../include
++ld -o efiemu64.o -r efiemu64_s.o efiemu64_c.o -nostdlib
index 0000000000000000000000000000000000000000,f6b13b19b95bd5031f7b030b2b5aeb31ed827b1a..0bf1e05f92e7808b060edff2642f758cd543cc82
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,322 +1,290 @@@
 -static int saved_char = ERR;
 -
+ /*  console.c -- Ncurses console for GRUB.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2005,2007,2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <config.h>
+ /* For compatibility.  */
+ #ifndef A_NORMAL
+ # define A_NORMAL     0
+ #endif /* ! A_NORMAL */
+ #ifndef A_STANDOUT
+ # define A_STANDOUT   0
+ #endif /* ! A_STANDOUT */
+ #include <grub/emu/console.h>
+ #include <grub/term.h>
+ #include <grub/types.h>
+ #if defined(HAVE_NCURSES_CURSES_H)
+ # include <ncurses/curses.h>
+ #elif defined(HAVE_NCURSES_H)
+ # include <ncurses.h>
+ #elif defined(HAVE_CURSES_H)
+ # include <curses.h>
+ #endif
+ static int grub_console_attr = A_NORMAL;
+ grub_uint8_t grub_console_cur_color = 7;
+ static const grub_uint8_t grub_console_standard_color = 0x7;
+ #define NUM_COLORS    8
+ static grub_uint8_t color_map[NUM_COLORS] =
+ {
+   COLOR_BLACK,
+   COLOR_BLUE,
+   COLOR_GREEN,
+   COLOR_CYAN,
+   COLOR_RED,
+   COLOR_MAGENTA,
+   COLOR_YELLOW,
+   COLOR_WHITE
+ };
+ static int use_color;
+ static void
+ grub_ncurses_putchar (struct grub_term_output *term __attribute__ ((unused)),
+                     const struct grub_unicode_glyph *c)
+ {
+   addch (c->base | grub_console_attr);
+ }
+ static void
+ grub_ncurses_setcolorstate (struct grub_term_output *term,
+                           grub_term_color_state state)
+ {
+   switch (state)
+     {
+     case GRUB_TERM_COLOR_STANDARD:
+       grub_console_cur_color = grub_console_standard_color;
+       grub_console_attr = A_NORMAL;
+       break;
+     case GRUB_TERM_COLOR_NORMAL:
+       grub_console_cur_color = term->normal_color;
+       grub_console_attr = A_NORMAL;
+       break;
+     case GRUB_TERM_COLOR_HIGHLIGHT:
+       grub_console_cur_color = term->highlight_color;
+       grub_console_attr = A_STANDOUT;
+       break;
+     default:
+       break;
+     }
+   if (use_color)
+     {
+       grub_uint8_t fg, bg;
+       fg = (grub_console_cur_color & 7);
+       bg = (grub_console_cur_color >> 4) & 7;
+       grub_console_attr = (grub_console_cur_color & 8) ? A_BOLD : A_NORMAL;
+       color_set ((bg << 3) + fg, 0);
+     }
+ }
 -grub_ncurses_checkkey (struct grub_term_input *term __attribute__ ((unused)))
+ static int
 -  /* Check for SAVED_CHAR. This should not be true, because this
 -     means checkkey is called twice continuously.  */
 -  if (saved_char != ERR)
 -    return saved_char;
 -
++grub_ncurses_getkey (struct grub_term_input *term __attribute__ ((unused)))
+ {
+   int c;
 -  /* If C is not ERR, then put it back in the input queue.  */
 -  if (c != ERR)
 -    {
 -      saved_char = c;
 -      return c;
 -    }
 -
 -  return -1;
 -}
 -
 -static int
 -grub_ncurses_getkey (struct grub_term_input *term __attribute__ ((unused)))
 -{
 -  int c;
 -
 -  /* If checkkey has already got a character, then return it.  */
 -  if (saved_char != ERR)
 -    {
 -      c = saved_char;
 -      saved_char = ERR;
 -    }
 -  else
 -    {
 -      wtimeout (stdscr, -1);
 -      c = getch ();
 -    }
+   wtimeout (stdscr, 100);
+   c = getch ();
 -    .checkkey = grub_ncurses_checkkey,
+   switch (c)
+     {
++    case ERR:
++      return -1;
+     case KEY_LEFT:
+       c = GRUB_TERM_LEFT;
+       break;
+     case KEY_RIGHT:
+       c = GRUB_TERM_RIGHT;
+       break;
+     case KEY_UP:
+       c = GRUB_TERM_UP;
+       break;
+     case KEY_DOWN:
+       c = GRUB_TERM_DOWN;
+       break;
+     case KEY_IC:
+       c = 24;
+       break;
+     case KEY_DC:
+       c = GRUB_TERM_DC;
+       break;
+     case KEY_BACKSPACE:
+       /* XXX: For some reason ncurses on xterm does not return
+        KEY_BACKSPACE.  */
+     case 127:
+       c = GRUB_TERM_BACKSPACE;
+       break;
+     case KEY_HOME:
+       c = GRUB_TERM_HOME;
+       break;
+     case KEY_END:
+       c = GRUB_TERM_END;
+       break;
+     case KEY_NPAGE:
+       c = GRUB_TERM_NPAGE;
+       break;
+     case KEY_PPAGE:
+       c = GRUB_TERM_PPAGE;
+       break;
+     }
+   return c;
+ }
+ static grub_uint16_t
+ grub_ncurses_getxy (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   int x;
+   int y;
+   getyx (stdscr, y, x);
+   return (x << 8) | y;
+ }
+ static grub_uint16_t
+ grub_ncurses_getwh (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   int x;
+   int y;
+   getmaxyx (stdscr, y, x);
+   return (x << 8) | y;
+ }
+ static void
+ grub_ncurses_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+                    grub_uint8_t x, grub_uint8_t y)
+ {
+   move (y, x);
+ }
+ static void
+ grub_ncurses_cls (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   clear ();
+   refresh ();
+ }
+ static void
+ grub_ncurses_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+                       int on)
+ {
+   curs_set (on ? 1 : 0);
+ }
+ static void
+ grub_ncurses_refresh (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   refresh ();
+ }
+ static grub_err_t
+ grub_ncurses_init (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   initscr ();
+   raw ();
+   noecho ();
+   scrollok (stdscr, TRUE);
+   nonl ();
+   intrflush (stdscr, FALSE);
+   keypad (stdscr, TRUE);
+   if (has_colors ())
+     {
+       start_color ();
+       if ((COLORS >= NUM_COLORS) && (COLOR_PAIRS >= NUM_COLORS * NUM_COLORS))
+         {
+           int i, j, n;
+           n = 0;
+           for (i = 0; i < NUM_COLORS; i++)
+             for (j = 0; j < NUM_COLORS; j++)
+               init_pair(n++, color_map[j], color_map[i]);
+           use_color = 1;
+         }
+     }
+   return 0;
+ }
+ static grub_err_t
+ grub_ncurses_fini (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   endwin ();
+   return 0;
+ }
\f
+ static struct grub_term_input grub_ncurses_term_input =
+   {
+     .name = "console",
+     .getkey = grub_ncurses_getkey,
+   };
+ static struct grub_term_output grub_ncurses_term_output =
+   {
+     .name = "console",
+     .init = grub_ncurses_init,
+     .fini = grub_ncurses_fini,
+     .putchar = grub_ncurses_putchar,
+     .getxy = grub_ncurses_getxy,
+     .getwh = grub_ncurses_getwh,
+     .gotoxy = grub_ncurses_gotoxy,
+     .cls = grub_ncurses_cls,
+     .setcolorstate = grub_ncurses_setcolorstate,
+     .setcursor = grub_ncurses_setcursor,
+     .refresh = grub_ncurses_refresh,
+     .flags = GRUB_TERM_CODE_TYPE_ASCII
+   };
+ void
+ grub_console_init (void)
+ {
+   grub_term_register_output ("console", &grub_ncurses_term_output);
+   grub_term_register_input ("console", &grub_ncurses_term_input);
+ }
+ void
+ grub_console_fini (void)
+ {
+   grub_ncurses_fini (&grub_ncurses_term_output);
+ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed57c43ca36ab3f1764d3b0b8f18d41d5ff6cad9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++
++/*
++ * 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.
++ */
++
++/*
++ *  This is the area for all of the special variables.
++ */
++
++      .p2align        2       /* force 4-byte alignment */
++
++/*
++ * 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
++VARIABLE(grub_linux_is_bzimage)
++      .long   0
++
++FUNCTION(grub_linux16_real_boot)
++      /* Must be done before zImage copy.  */
++      call    EXT_C(grub_dl_unload_all)
++
++      movl    EXT_C(grub_linux_is_bzimage), %ebx
++      test    %ebx, %ebx
++      jne bzimage
++
++      /* 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
++
++bzimage:
++      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
++
index 0000000000000000000000000000000000000000,7ae9017129c31041598371cb9219bdbfc30f4a13..58fb2187350110e5cbd39c396b71fd74f71cf33a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2071 +1,2022 @@@
 -/* this table is used in translate_keycode below */
 -LOCAL (translation_table):
 -      .word   GRUB_CONSOLE_KEY_LEFT, GRUB_TERM_LEFT
 -      .word   GRUB_CONSOLE_KEY_RIGHT, GRUB_TERM_RIGHT
 -      .word   GRUB_CONSOLE_KEY_UP, GRUB_TERM_UP
 -      .word   GRUB_CONSOLE_KEY_DOWN, GRUB_TERM_DOWN
 -      .word   GRUB_CONSOLE_KEY_HOME, GRUB_TERM_HOME
 -      .word   GRUB_CONSOLE_KEY_END, GRUB_TERM_END
 -      .word   GRUB_CONSOLE_KEY_DC, GRUB_TERM_DC
 -      .word   GRUB_CONSOLE_KEY_BACKSPACE, GRUB_TERM_BACKSPACE
 -      .word   GRUB_CONSOLE_KEY_PPAGE, GRUB_TERM_PPAGE
 -      .word   GRUB_CONSOLE_KEY_NPAGE, GRUB_TERM_NPAGE
 -      .word   0
 -
 -/*
 - * translate_keycode translates the key code %dx to an ascii code.
 - */
 -      .code16
 -
 -translate_keycode:
 -      pushw   %bx
 -      pushw   %si
 -
 -#ifdef __APPLE__
 -      movw    $(ABS(LOCAL (translation_table)) - 0x10000), %si
 -#else
 -      movw    $ABS(LOCAL (translation_table)), %si
 -#endif
 -
 -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
 -
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009 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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ /*
+  * 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 is 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/cpu/linux.h>
+ #include <grub/machine/kernel.h>
+ #include <grub/term.h>
+ #include <multiboot.h>
+ #include <multiboot2.h>
+ #define ABS(x)        ((x) - LOCAL (base) + 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:
+ LOCAL (base):
+       /*
+        *  Guarantee that "main" is loaded at 0x0:0x8200.
+        */
+ #ifdef __APPLE__
+       ljmp $0, $(ABS(LOCAL (codestart)) - 0x10000)
+ #else
+       ljmp $0, $ABS(LOCAL (codestart))
+ #endif
+       /*
+        *  Compatibility version number
+        *
+        *  These MUST be at byte offset 6 and 7 of the executable
+        *  DO NOT MOVE !!!
+        */
+       . = _start + 0x6
+       .byte   GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+       /*
+        *  This is a special data area 8 bytes from the beginning.
+        */
+       . = _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)
+       /* to be filled by grub-mkimage */
+       /*
+        *  Leave some breathing room for the prefix.
+        */
+       . = _start + GRUB_KERNEL_MACHINE_DATA_END
+ #ifdef APPLE_CC
+ bss_start:
+       .long 0
+ bss_end:
+       .long 0
+ #endif
+ /*
+  * 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.
+  */
+       .p2align        2       /* force 4-byte alignment */
+ 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
+       movl    $GRUB_MEMORY_MACHINE_PROT_STACK, %ebp
+       movl    %ebp, %esp
+       /* 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
+         movb    $0xFF, %dh
+       /* enter the usual booting */
+       call    prot_to_real
+       .code16
+ /* the real mode code continues... */
+ LOCAL (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 the boot drive */
+       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)
+ #ifdef ENABLE_LZMA
+       movl    $GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, %edi
+       movl    $(_start + GRUB_KERNEL_MACHINE_RAW_SIZE), %esi
+       pushl   %edi
+       pushl   %esi
+       movl    EXT_C(grub_kernel_image_size), %ecx
+       addl    EXT_C(grub_total_module_size), %ecx
+       subl    $GRUB_KERNEL_MACHINE_RAW_SIZE, %ecx
+       pushl   %ecx
+       leal    (%edi, %ecx), %ebx
+       call    _LzmaDecodeA
+       /* _LzmaDecodeA clears DF, so no need to run cld */
+       popl    %ecx
+       popl    %edi
+       popl    %esi
+ #endif
+       /* copy back the decompressed part (except the modules) */
+       subl    EXT_C(grub_total_module_size), %ecx
+       rep
+       movsb
+ #if 0
+       /* 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, %esi
+       decl    %esi
+       movl    $END_SYMBOL, %edi
+       addl    %ecx, %edi
+       decl    %edi
+       std
+       rep
+       movsb
+ #endif
+ #ifdef APPLE_CC
+       /* clean out the bss */
+       bss_start_abs = ABS (bss_start)
+       bss_end_abs = ABS (bss_end)
+       movl    bss_start_abs, %edi
+       /* compute the bss length */
+       movl    bss_end_abs, %ecx
+       subl    %edi, %ecx
+ #else
+       /* clean out the bss */
+       movl    $BSS_START_SYMBOL, %edi
+       /* compute the bss length */
+       movl    $END_SYMBOL, %ecx
+       subl    %edi, %ecx
+ #endif
+       /* 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.
+  */
+ VARIABLE(grub_boot_drive)
+       .byte   0
+       .p2align        2       /* force 4-byte alignment */
+ #include "../realmode.S"
+ /*
+  * 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_system_control_port_a
+       ret
+ gate_a20_try_system_control_port_a:
+       /*
+        * In macbook, the keyboard test would hang the machine, so we move
+        * this forward.
+        */
+       /* 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
+       jnz     gate_a20_try_keyboard_controller
+ 7:    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
+       /* everything failed, so restart from the beginning */
+       jnz     gate_a20_try_bios
+       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
+ #ifdef ENABLE_LZMA
+ #include "lzma_decode.S"
+ #endif
+ /*
+  * The code beyond this point is compressed.  Assert that the uncompressed
+  * code fits GRUB_KERNEL_MACHINE_RAW_SIZE.
+  */
+       . = _start + GRUB_KERNEL_MACHINE_RAW_SIZE
+       /*
+        * 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_exit()
+  *
+  * Exit the system.
+  */
+ FUNCTION(grub_exit)
+       call    prot_to_real
+       .code16
+       /* Tell the BIOS a boot failure. If this does not work, reboot.  */
+       int     $0x18
+       jmp     cold_reboot
+       .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
+       /* Turn off Gate A20 */
+       xorl    %eax, %eax
+       call    EXT_C(grub_gate_a20)
+       /* set up to pass boot drive */
+       popl    %edx
+       /* ESI must point to a partition table entry */
+       popl    %esi
+       call    prot_to_real
+       .code16
+       ljmp    $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
+       .code32
+ /*
+  *   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 */
+       /* 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_cdinfo_int13_extensions (int drive, void *cdrp)
+  *
+  *   Return the cdrom information of DRIVE in CDRP. If an error occurs,
+  *   then return non-zero, otherwise zero.
+  */
+ FUNCTION(grub_biosdisk_get_cdinfo_int13_extensions)
+       movw    $0x4B01, %cx
+       jmp     1f
+ /*
+  *   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)
+       movb    $0x48, %ch
+ 1:
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %esi
+       /* compute the address of drive parameters */
+       movw    %dx, %si
+       andl    $0xf, %esi
+       shrl    $4, %edx
+       movw    %dx, %bx        /* save the segment into %bx */
+       /* drive */
+       movb    %al, %dl
+       /* enter real mode */
+       call    prot_to_real
+       .code16
+       movw    %cx, %ax
+       movw    %bx, %ds
+       int     $0x13           /* do the operation */
+       jc      noclean
+       /* Clean return value if carry isn't set to workaround
+       some buggy BIOSes.  */
+       xor     %ax, %ax
+ noclean:
+       movb    %ah, %bl        /* save return value in %bl */
+       /* 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 */
+       jc      noclean2
+       /* Clean return value if carry isn't set to workaround
+       some buggy BIOSes.  */
+       xor     %ax, %ax
+ noclean2:
+       /* 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
+ /*    Apple's cc jumps few bytes before the correct
+       label in this context. Hence nops. */
+ #ifdef APPLE_CC
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+ #endif
+ 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_putchar (const struct grub_unicode_glyph *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 OUTPUT BIOS call doesn't
+  * support setting a background attribute.
+  */
+ FUNCTION(grub_console_putchar)
+       /* Retrieve the base character.  */
+       movl    0(%edx), %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
++LOCAL(bypass_table):
++      .word 0x0100 | '\e',0x0f00 | '\t', 0x0e00 | '\b', 0x1c00 | '\r'
++      .word 0x1c00 | '\n'
++LOCAL(bypass_table_end):
++
+ /*
+  * int grub_console_getkey (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
+  * BIOS call "INT 16H Function 00H" to read character from keyboard
+  *    Call with       %ah = 0x0
+  *    Return:         %ah = keyboard scan code
+  *                    %al = ASCII character
+  */
 -1:
+ FUNCTION(grub_console_getkey)
+       pushl   %ebp
+       call    prot_to_real
+       .code16
+       /*
+        * Due to a bug in apple's bootcamp implementation, INT 16/AH = 0 would
+        * cause the machine to hang at the second keystroke. However, we can
+        * work around this problem by ensuring the presence of keystroke with
+        * INT 16/AH = 1 before calling INT 16/AH = 0.
+        */
 -      jnz     2f
 -      hlt
 -      jmp     1b
 -
 -2:
+       movb    $1, %ah
+       int     $0x16
 -      call    translate_keycode
++      jz      notpending
+       movb    $0, %ah
+       int     $0x16
++      xorl    %edx, %edx
+       movw    %ax, %dx                /* real_to_prot uses %eax */
 -      movw    %dx, %ax
+       DATA32  call    real_to_prot
+       .code32
 -      popl    %ebp
 -      ret
++      movl    $0xff, %eax
++      testl   %eax, %edx
++      jz      1f
 -/*
 - * 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
++      andl    %edx, %eax
++      cmp     %eax, 0x20
++      ja      2f
++      movl    %edx, %eax
++      leal    LOCAL(bypass_table), %esi
++      movl    $((LOCAL(bypass_table_end) - LOCAL(bypass_table)) / 2), %ecx
++      repne cmpsw
++      jz      3f
++      addl    $('a' - 1 | GRUB_TERM_CTRL), %eax
++      jmp     2f
++3:
++      andl    $0xff, %eax
++      jmp 2f
 -      call    prot_to_real    /* enter real mode */
++1:    movl    %edx, %eax
++      shrl    $8, %eax
++      orl     $GRUB_TERM_EXTENDED, %eax
++2:    
++      popl    %ebp
++      ret
 -
 -      movb    $0x1, %ah
 -      int     $0x16
 -
 -      jz      notpending
 -
 -      movw    %ax, %dx
 -      DATA32  jmp     pending
 -
 -notpending:
 -      decl    %edx
 -
 -pending:
++notpending:   
+       .code16
 -
 -      movl    %edx, %eax
 -
 -      popl    %ebp
 -      ret
+       DATA32  call    real_to_prot
+       .code32
++#if GRUB_TERM_NO_KEY != 0
++#error Fix this asm code
++#endif
++      jmp 2b
+ /*
+  * 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    %cl, %dh        /* %dh = y */
+       /* %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   %edx
+       /* 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_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
+ /*
+  * grub_vbe_bios_status_t grub_vbe_get_controller_info (struct grub_vbe_info_block *controller_info)
+  *
+  * Register allocations for parameters:
+  * %eax               *controller_info
+  */
+ FUNCTION(grub_vbe_bios_get_controller_info)
+       pushl   %ebp
+       pushl   %edi
+       pushl   %edx
+       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.  */
+       pop     %edx
+       popl    %edi
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_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_bios_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_bios_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_bios_set_mode)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       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    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_get_mode (grub_uint32_t *mode)
+  *
+  * Register allocations for parameters:
+  * %eax               *mode
+  */
+ FUNCTION(grub_vbe_bios_get_mode)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       pushl   %edx
+       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    %edx
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_getset_dac_palette_width (int set, int *dac_mask_size)
+  *
+  * Register allocations for parameters:
+  * %eax               set
+  * %edx               *dac_mask_size
+  */
+ FUNCTION(grub_vbe_bios_getset_dac_palette_width)
+       pushl   %ebp
+       pushl   %ebx
+       xorl    %ebx, %ebx
+       /* If we only want to fetch the value, set %bl to 1.  */
+       testl   %eax, %eax
+       jne     1f
+       incb    %bl
+ 1:
+       /* Put desired width in %bh.  */
+       movl    (%edx), %eax
+       movb    %al, %bh
+       call    prot_to_real
+       .code16
+       movw    $0x4f08, %ax
+       int     $0x10
+       movw    %ax, %cx        /* real_to_prot destroys %eax.  */
+       DATA32 call     real_to_prot
+       .code32
+       /* Move result back to *dac_mask_size.  */
+       xorl    %eax, %eax
+       movb    %bh, %al
+       movl    %eax, (%edx)
+       /* Return value in %eax.  */
+       movw    %cx, %ax
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_set_memory_window (grub_uint32_t window,
+  *                                                  grub_uint32_t position);
+  *
+  * Register allocations for parameters:
+  * %eax               window
+  * %edx               position
+  */
+ FUNCTION(grub_vbe_bios_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_bios_get_memory_window (grub_uint32_t window,
+  *                                                  grub_uint32_t *position);
+  *
+  * Register allocations for parameters:
+  * %eax               window
+  * %edx               *position
+  */
+ FUNCTION(grub_vbe_bios_get_memory_window)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       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    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_set_scanline_length (grub_uint32_t length)
+  *
+  * Register allocations for parameters:
+  * %eax               length
+  */
+ FUNCTION(grub_vbe_bios_set_scanline_length)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edx
+       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    %edx
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_get_scanline_length (grub_uint32_t *length)
+  *
+  * Register allocations for parameters:
+  * %eax               *length
+  */
+ FUNCTION(grub_vbe_bios_get_scanline_length)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       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    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_set_display_start (grub_uint32_t x,
+  *                                                  grub_uint32_t y)
+  *
+  * Register allocations for parameters:
+  * %eax               x
+  * %edx               y
+  */
+ FUNCTION(grub_vbe_bios_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_bios_get_display_start (grub_uint32_t *x,
+  *                                                  grub_uint32_t *y)
+  *
+  * Register allocations for parameters:
+  * %eax               *x
+  * %edx               *y
+  */
+ FUNCTION(grub_vbe_bios_get_display_start)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       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    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * grub_vbe_status_t grub_vbe_bios_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_bios_set_palette_data)
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       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    %edi
+       popl    %ebx
+       popl    %ebp
+       ret
+ pxe_rm_entry:
+       .long   0
+ /*
+  * struct grub_pxenv *grub_pxe_scan (void);
+  */
+ FUNCTION(grub_pxe_scan)
+       pushl   %ebp
+       pushl   %ebx
+       xorl    %ebx, %ebx
+       xorl    %ecx, %ecx
+       call    prot_to_real
+       .code16
+       pushw   %es
+       movw    $0x5650, %ax
+       int     $0x1A
+       cmpw    $0x564E, %ax
+       jnz     1f
+       cmpl    $0x4E455850, %es:(%bx)          /* PXEN(V+)  */
+       jnz     1f
+       cmpw    $0x201, %es:6(%bx)              /* API version  */
+       jb      1f
+       lesw    %es:0x28(%bx), %bx              /* !PXE structure  */
+       cmpl    $0x45585021, %es:(%bx)          /* !PXE  */
+       jnz     1f
+       movw    %es, %cx
+       jmp     2f
+ 1:
+       xorw    %bx, %bx
+       xorw    %cx, %cx
+ 2:
+       popw    %es
+       DATA32 call     real_to_prot
+       .code32
+       xorl    %eax, %eax
+       leal    (%eax, %ecx, 4), %ecx
+       leal    (%ebx, %ecx, 4), %eax           /* eax = ecx * 16 + ebx  */
+       orl     %eax, %eax
+       jz      1f
+       movl    0x10(%eax), %ecx
+       movl    %ecx, pxe_rm_entry
+ 1:
+       popl    %ebx
+       popl    %ebp
+       ret
+ /*
+  * int grub_pxe_call (int func, void* data);
+  */
+ FUNCTION(grub_pxe_call)
+       pushl   %ebp
+       movl    %esp, %ebp
+       pushl   %esi
+       pushl   %edi
+       pushl   %ebx
+       movl    %eax, %ecx
+       movl    %edx, %eax
+       andl    $0xF, %eax
+       shrl    $4, %edx
+       shll    $16, %edx
+       addl    %eax, %edx
+       movl    pxe_rm_entry, %ebx
+       call    prot_to_real
+       .code16
+       pushl   %ebx
+       pushl   %edx
+       pushw   %cx
+       movw    %sp, %bx
+       lcall   *%ss:6(%bx)
+       cld
+       addw    $10, %sp
+       movw    %ax, %cx
+       DATA32  call    real_to_prot
+       .code32
+       movzwl  %cx, %eax
+       popl    %ebx
+       popl    %edi
+       popl    %esi
+       popl    %ebp
+       ret
index 0000000000000000000000000000000000000000,523f9028296c3cc8adb63395865699500dc94f9f..2eceff78d70d9fb6cca984171ccb81b9898b93b3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,245 +1,247 @@@
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2009,2010  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/kernel.h>
+ #include <grub/misc.h>
+ #include <grub/env.h>
+ #include <grub/time.h>
+ #include <grub/types.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/time.h>
+ #include <grub/machine/kernel.h>
+ #include <grub/machine/memory.h>
+ #include <grub/mips/loongson.h>
+ #include <grub/cs5536.h>
+ #include <grub/term.h>
+ #include <grub/machine/ec.h>
+ extern void grub_video_sm712_init (void);
+ extern void grub_video_init (void);
+ extern void grub_bitmap_init (void);
+ extern void grub_font_init (void);
+ extern void grub_gfxterm_init (void);
+ extern void grub_at_keyboard_init (void);
+ extern void grub_serial_init (void);
+ extern void grub_terminfo_init (void);
++extern void grub_keylayouts_init (void);
+ /* FIXME: use interrupt to count high.  */
+ grub_uint64_t
+ grub_get_rtc (void)
+ {
+   static grub_uint32_t high = 0;
+   static grub_uint32_t last = 0;
+   grub_uint32_t low;
+   asm volatile ("mfc0 %0, " GRUB_CPU_LOONGSON_COP0_TIMER_COUNT : "=r" (low));
+   if (low < last)
+     high++;
+   last = low;
+   return (((grub_uint64_t) high) << 32) | low;
+ }
+ grub_err_t
+ grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t,
+                                                        grub_uint64_t,
+                                                        grub_uint32_t))
+ {
+   hook (GRUB_ARCH_LOWMEMPSTART, grub_arch_memsize << 20,
+       GRUB_MACHINE_MEMORY_AVAILABLE);
+   hook (GRUB_ARCH_HIGHMEMPSTART, grub_arch_highmemsize << 20,
+       GRUB_MACHINE_MEMORY_AVAILABLE);
+   return GRUB_ERR_NONE;
+ }
+ static void
+ init_pci (void)
+ {
+   auto int NESTED_FUNC_ATTR set_card (grub_pci_device_t dev, grub_pci_id_t pciid);
+   int NESTED_FUNC_ATTR set_card (grub_pci_device_t dev, grub_pci_id_t pciid)
+   {
+     grub_pci_address_t addr;
+     /* FIXME: autoscan for BARs and devices.  */
+     switch (pciid)
+       {
+       case GRUB_YEELOONG_OHCI_PCIID:
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+       grub_pci_write (addr, 0x5025000);
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+       grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE
+                            | GRUB_PCI_COMMAND_PARITY_ERROR
+                            | GRUB_PCI_COMMAND_BUS_MASTER
+                            | GRUB_PCI_COMMAND_MEM_ENABLED);
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS);
+       grub_pci_write_word (addr, 0x0200 | GRUB_PCI_STATUS_CAPABILITIES);
+       break;
+       case GRUB_YEELOONG_EHCI_PCIID:
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+       grub_pci_write (addr, 0x5026000);
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+       grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE
+                            | GRUB_PCI_COMMAND_PARITY_ERROR
+                            | GRUB_PCI_COMMAND_BUS_MASTER
+                            | GRUB_PCI_COMMAND_MEM_ENABLED);
+       addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS);
+       grub_pci_write_word (addr, (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT)
+                            | GRUB_PCI_STATUS_CAPABILITIES);
+       break;
+       }
+     return 0;
+   }
+   *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_LO) = 0x8000000c;
+   *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_HI) = 0xffffffff;
+   /* Setup PCI controller.  */
+   *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+                               + GRUB_PCI_REG_COMMAND))
+     = GRUB_PCI_COMMAND_PARITY_ERROR | GRUB_PCI_COMMAND_BUS_MASTER
+     | GRUB_PCI_COMMAND_MEM_ENABLED;
+   *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+                               + GRUB_PCI_REG_STATUS))
+     = (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT)
+     | GRUB_PCI_STATUS_FAST_B2B_CAPABLE | GRUB_PCI_STATUS_66MHZ_CAPABLE
+     | GRUB_PCI_STATUS_CAPABILITIES;
+   *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+                               + GRUB_PCI_REG_CACHELINE)) = 0xff;
+   *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER 
+                               + GRUB_PCI_REG_ADDRESS_REG0))
+     = 0x80000000 | GRUB_PCI_ADDR_MEM_TYPE_64 | GRUB_PCI_ADDR_MEM_PREFETCH;
+   *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER 
+                               + GRUB_PCI_REG_ADDRESS_REG1)) = 0;
+   grub_pci_iterate (set_card);
+ }
+ void
+ grub_machine_init (void)
+ {
+   grub_addr_t modend;
+   /* FIXME: measure this.  */
+   if (grub_arch_busclock == 0)
+     {
+       grub_arch_busclock = 66000000;
+       grub_arch_cpuclock = 797000000;
+     }
+   grub_install_get_time_ms (grub_rtc_get_time_ms);
+   if (grub_arch_memsize == 0)
+     {
+       grub_port_t smbbase;
+       grub_err_t err;
+       grub_pci_device_t dev;
+       struct grub_smbus_spd spd;
+       unsigned totalmem;
+       int i;
+       if (!grub_cs5536_find (&dev))
+       grub_fatal ("No CS5536 found\n");
+       err = grub_cs5536_init_smbus (dev, 0x7ff, &smbbase);
+       if (err)
+       grub_fatal ("Couldn't init SMBus: %s\n", grub_errmsg);
+       /* Yeeloong has only one memory slot.  */
+       err = grub_cs5536_read_spd (smbbase, GRUB_SMB_RAM_START_ADDR, &spd);
+       if (err)
+       grub_fatal ("Couldn't read SPD: %s\n", grub_errmsg);
+       for (i = 5; i < 13; i++)
+       if (spd.ddr2.rank_capacity & (1 << (i & 7)))
+         break;
+       /* Something is wrong.  */
+       if (i == 13)
+       totalmem = 256;
+       else
+       totalmem = ((spd.ddr2.num_of_ranks
+                    & GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_MASK) + 1) << (i + 2);
+       
+       if (totalmem >= 256)
+       {
+         grub_arch_memsize = 256;
+         grub_arch_highmemsize = totalmem - 256;
+       }
+       else
+       {
+         grub_arch_memsize = (totalmem >> 20);
+         grub_arch_highmemsize = 0;
+       }
+       grub_cs5536_init_geode (dev);
+       init_pci ();
+     }
+   modend = grub_modules_get_end ();
+   grub_mm_init_region ((void *) modend, (grub_arch_memsize << 20)
+                      - (modend - GRUB_ARCH_LOWMEMVSTART));
+   /* FIXME: use upper memory as well.  */
+   /* Initialize output terminal (can't be done earlier, as gfxterm
+      relies on a working heap.  */
+   grub_video_init ();
+   grub_video_sm712_init ();
+   grub_bitmap_init ();
+   grub_font_init ();
+   grub_gfxterm_init ();
++  grub_keylayouts_init ();
+   grub_at_keyboard_init ();
+   grub_terminfo_init ();
+   grub_serial_init ();
+ }
+ void
+ grub_machine_fini (void)
+ {
+ }
+ void
+ grub_halt (void)
+ {
+   grub_outb (grub_inb (GRUB_CPU_LOONGSON_GPIOCFG)
+            & ~GRUB_CPU_LOONGSON_SHUTDOWN_GPIO, GRUB_CPU_LOONGSON_GPIOCFG);
+   grub_printf ("Shutdown failed\n");
+   grub_refresh ();
+   while (1);
+ }
+ void
+ grub_exit (void)
+ {
+   grub_halt ();
+ }
+ void
+ grub_reboot (void)
+ {
+   grub_write_ec (GRUB_MACHINE_EC_COMMAND_REBOOT);
+   grub_printf ("Reboot failed\n");
+   grub_refresh ();
+   while (1);
+ }
index 0000000000000000000000000000000000000000,531fcc8fa37f2b9c5348562b4a8b7fc801e3daec..a810085790c98fde482563ef84ca08453062d018
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,97 +1,97 @@@
 -  while ((c = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && c != '\r')
+ /* rescue_reader.c - rescue mode reader  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2009,2010  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/types.h>
+ #include <grub/reader.h>
+ #include <grub/parser.h>
+ #include <grub/misc.h>
+ #include <grub/term.h>
+ #include <grub/mm.h>
+ #define GRUB_RESCUE_BUF_SIZE  256
+ static char linebuf[GRUB_RESCUE_BUF_SIZE];
+ /* Prompt to input a command and read the line.  */
+ static grub_err_t
+ grub_rescue_read_line (char **line, int cont)
+ {
+   int c;
+   int pos = 0;
+   char str[4];
+   grub_printf ((cont) ? "> " : "grub rescue> ");
+   grub_memset (linebuf, 0, GRUB_RESCUE_BUF_SIZE);
++  while ((c = grub_getkey ()) != '\n' && c != '\r')
+     {
+       if (grub_isprint (c))
+       {
+         if (pos < GRUB_RESCUE_BUF_SIZE - 1)
+           {
+             str[0] = c;
+             str[1] = 0;
+             linebuf[pos++] = c;
+             grub_xputs (str);
+           }
+       }
+       else if (c == '\b')
+       {
+         if (pos > 0)
+           {
+             str[0] = c;
+             str[1] = ' ';
+             str[2] = c;
+             str[3] = 0;
+             linebuf[--pos] = 0;
+             grub_xputs (str);
+           }
+       }
+       grub_refresh ();
+     }
+   grub_xputs ("\n");
+   grub_refresh ();
+   *line = grub_strdup (linebuf);
+   return 0;
+ }
+ void
+ grub_rescue_run (void)
+ {
+   grub_printf ("Entering rescue mode...\n");
+   while (1)
+     {
+       char *line;
+       /* Print an error, if any.  */
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       grub_rescue_read_line (&line, 0);
+       if (! line || line[0] == '\0')
+       continue;
+       grub_rescue_parse_line (line, grub_rescue_read_line);
+       grub_free (line);
+     }
+ }
index 0000000000000000000000000000000000000000,360539e500f202a2177b5d20c5cf58088379d7f8..7b3593161bf660174ee2a0e13bb438a8f41054c4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,147 +1,130 @@@
 -int
 -grub_getkey (void)
 -{
 -  grub_term_input_t term;
 -
 -  grub_refresh ();
 -
 -  while (1)
 -    {
 -      if (grub_term_poll_usb)
 -      grub_term_poll_usb ();
 -
 -      FOR_ACTIVE_TERM_INPUTS(term)
 -      {
 -      int key = term->checkkey (term);
 -      if (key != -1)
 -        return term->getkey (term);
 -      }
 -
 -      grub_cpu_idle ();
 -    }
 -}
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2002,2003,2005,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/term.h>
+ #include <grub/err.h>
+ #include <grub/mm.h>
+ #include <grub/misc.h>
+ #include <grub/env.h>
+ #include <grub/time.h>
+ struct grub_term_output *grub_term_outputs_disabled;
+ struct grub_term_input *grub_term_inputs_disabled;
+ struct grub_term_output *grub_term_outputs;
+ struct grub_term_input *grub_term_inputs;
+ void (*grub_term_poll_usb) (void) = NULL;
+ /* Put a Unicode character.  */
+ static void
+ grub_putcode_dumb (grub_uint32_t code,
+                  struct grub_term_output *term)
+ {
+   struct grub_unicode_glyph c =
+     {
+       .base = code,
+       .variant = 0,
+       .attributes = 0,
+       .ncomb = 0,
+       .combining = 0,
+       .estimated_width = 1
+     };
+   if (code == '\t' && term->getxy)
+     {
+       int n;
+       n = 8 - ((term->getxy (term) >> 8) & 7);
+       while (n--)
+       grub_putcode_dumb (' ', term);
+       return;
+     }
+   (term->putchar) (term, &c);
+   if (code == '\n')
+     grub_putcode_dumb ('\r', term);
+ }
+ static void
+ grub_xputs_dumb (const char *str)
+ {
+   for (; *str; str++)
+     {
+       grub_term_output_t term;
+       grub_uint32_t code = *str;
+       if (code > 0x7f)
+       code = '?';
+       FOR_ACTIVE_TERM_OUTPUTS(term)
+       grub_putcode_dumb (code, term);
+     }
+ }
+ void (*grub_xputs) (const char *str) = grub_xputs_dumb;
 -    int key = term->checkkey (term);
 -    if (key != -1)
 -      return key;
++static int pending_key = GRUB_TERM_NO_KEY;
+ int
+ grub_checkkey (void)
+ {
+   grub_term_input_t term;
++  if (pending_key != GRUB_TERM_NO_KEY)
++    return pending_key;
++
+   if (grub_term_poll_usb)
+     grub_term_poll_usb ();
+   FOR_ACTIVE_TERM_INPUTS(term)
+   {
 -grub_getkeystatus (void)
++    pending_key = term->getkey (term);
++    if (pending_key != GRUB_TERM_NO_KEY)
++      return pending_key;
+   }
+   return -1;
+ }
+ int
 -  int status = 0;
 -  grub_term_input_t term;
++grub_getkey (void)
+ {
 -  if (grub_term_poll_usb)
 -    grub_term_poll_usb ();
 -
 -  FOR_ACTIVE_TERM_INPUTS(term)
 -  {
 -    if (term->getkeystatus)
 -      status |= term->getkeystatus (term);
 -  }
++  int ret;
 -  return status;
++  grub_refresh ();
++  grub_checkkey ();
++  while (pending_key == GRUB_TERM_NO_KEY)
++    {
++      grub_cpu_idle ();
++      grub_checkkey ();
++    }
++  ret = pending_key;
++  pending_key = GRUB_TERM_NO_KEY;
++  return ret;
+ }
++
+ void
+ grub_refresh (void)
+ {
+   struct grub_term_output *term;
+   FOR_ACTIVE_TERM_OUTPUTS(term)
+     grub_term_refresh (term);
+ }
index 0000000000000000000000000000000000000000,6bd038168e7c2fc55ef997c83bc039ce81340e97..f5768b8b5e6a3522e767ee5cf8d71258cd74a744
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,453 +1,453 @@@
 -      key = GRUB_TERM_ASCII_CHAR (grub_getkey ()); 
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006
+  *                2007, 2008, 2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/crypto.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/term.h>
+ struct grub_crypto_hmac_handle
+ {
+   const struct gcry_md_spec *md;
+   void *ctx;
+   void *opad;
+ };
+ static gcry_cipher_spec_t *grub_ciphers = NULL;
+ static gcry_md_spec_t *grub_digests = NULL;
+ void (*grub_crypto_autoload_hook) (const char *name) = NULL;
+ /* Based on libgcrypt-1.4.4/src/misc.c.  */
+ void
+ grub_burn_stack (grub_size_t size)
+ {
+   char buf[64];
+   grub_memset (buf, 0, sizeof (buf));
+   if (size > sizeof (buf))
+     grub_burn_stack (size - sizeof (buf));
+ }
+ void 
+ grub_cipher_register (gcry_cipher_spec_t *cipher)
+ {
+   cipher->next = grub_ciphers;
+   grub_ciphers = cipher;
+ }
+ void
+ grub_cipher_unregister (gcry_cipher_spec_t *cipher)
+ {
+   gcry_cipher_spec_t **ciph;
+   for (ciph = &grub_ciphers; *ciph; ciph = &((*ciph)->next))
+     if (*ciph == cipher)
+       {
+       *ciph = (*ciph)->next;
+       break;
+       }
+ }
+ void 
+ grub_md_register (gcry_md_spec_t *digest)
+ {
+   digest->next = grub_digests;
+   grub_digests = digest;
+ }
+ void 
+ grub_md_unregister (gcry_md_spec_t *cipher)
+ {
+   gcry_md_spec_t **ciph;
+   for (ciph = &grub_digests; *ciph; ciph = &((*ciph)->next))
+     if (*ciph == cipher)
+       {
+       *ciph = (*ciph)->next;
+       break;
+       }
+ }
+ void
+ grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
+                 grub_size_t inlen)
+ {
+   grub_uint8_t ctx[hash->contextsize];
+   hash->init (&ctx);
+   hash->write (&ctx, in, inlen);
+   hash->final (&ctx);
+   grub_memcpy (out, hash->read (&ctx), hash->mdlen);
+ }
+ const gcry_md_spec_t *
+ grub_crypto_lookup_md_by_name (const char *name)
+ {
+   const gcry_md_spec_t *md;
+   int first = 1;
+   while (1)
+     {
+       for (md = grub_digests; md; md = md->next)
+       if (grub_strcasecmp (name, md->name) == 0)
+         return md;
+       if (grub_crypto_autoload_hook && first)
+       grub_crypto_autoload_hook (name);
+       else
+       return NULL;
+       first = 0;
+     }
+ }
+ const gcry_cipher_spec_t *
+ grub_crypto_lookup_cipher_by_name (const char *name)
+ {
+   const gcry_cipher_spec_t *ciph;
+   int first = 1;
+   while (1)
+     {
+       for (ciph = grub_ciphers; ciph; ciph = ciph->next)
+       {
+         const char **alias;
+         if (grub_strcasecmp (name, ciph->name) == 0)
+           return ciph;
+         if (!ciph->aliases)
+           continue;
+         for (alias = ciph->aliases; *alias; alias++)
+           if (grub_strcasecmp (name, *alias) == 0)
+             return ciph;
+       }
+       if (grub_crypto_autoload_hook && first)
+       grub_crypto_autoload_hook (name);
+       else
+       return NULL;
+       first = 0;
+     }
+ }
+ grub_crypto_cipher_handle_t
+ grub_crypto_cipher_open (const struct gcry_cipher_spec *cipher)
+ {
+   grub_crypto_cipher_handle_t ret;
+   ret = grub_malloc (sizeof (*ret) + cipher->contextsize);
+   if (!ret)
+     return NULL;
+   ret->cipher = cipher;
+   return ret;
+ }
+ gcry_err_code_t
+ grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher,
+                           const unsigned char *key,
+                           unsigned keylen)
+ {
+   return cipher->cipher->setkey (cipher->ctx, key, keylen);
+ }
+ void
+ grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher)
+ {
+   grub_free (cipher);
+ }
+ void
+ grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size)
+ {
+   const grub_uint8_t *in1ptr = in1, *in2ptr = in2;
+   grub_uint8_t *outptr = out;
+   while (size--)
+     {
+       *outptr = *in1ptr ^ *in2ptr;
+       in1ptr++;
+       in2ptr++;
+       outptr++;
+     }
+ }
+ gcry_err_code_t
+ grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher,
+                        void *out, void *in, grub_size_t size)
+ {
+   grub_uint8_t *inptr, *outptr, *end;
+   if (!cipher->cipher->decrypt)
+     return GPG_ERR_NOT_SUPPORTED;
+   if (size % cipher->cipher->blocksize != 0)
+     return GPG_ERR_INV_ARG;
+   end = (grub_uint8_t *) in + size;
+   for (inptr = in, outptr = out; inptr < end;
+        inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize)
+     cipher->cipher->decrypt (cipher->ctx, outptr, inptr);
+   return GPG_ERR_NO_ERROR;
+ }
+ gcry_err_code_t
+ grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher,
+                        void *out, void *in, grub_size_t size)
+ {
+   grub_uint8_t *inptr, *outptr, *end;
+   if (!cipher->cipher->encrypt)
+     return GPG_ERR_NOT_SUPPORTED;
+   if (size % cipher->cipher->blocksize != 0)
+     return GPG_ERR_INV_ARG;
+   end = (grub_uint8_t *) in + size;
+   for (inptr = in, outptr = out; inptr < end;
+        inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize)
+     cipher->cipher->encrypt (cipher->ctx, outptr, inptr);
+   return GPG_ERR_NO_ERROR;
+ }
+ gcry_err_code_t
+ grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher,
+                        void *out, void *in, grub_size_t size,
+                        void *iv_in)
+ {
+   grub_uint8_t *inptr, *outptr, *end;
+   void *iv;
+   if (!cipher->cipher->decrypt)
+     return GPG_ERR_NOT_SUPPORTED;
+   if (size % cipher->cipher->blocksize != 0)
+     return GPG_ERR_INV_ARG;
+   end = (grub_uint8_t *) in + size;
+   iv = iv_in;
+   for (inptr = in, outptr = out; inptr < end;
+        inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize)
+     {
+       grub_crypto_xor (outptr, inptr, iv, cipher->cipher->blocksize);
+       cipher->cipher->encrypt (cipher->ctx, outptr, outptr);
+       iv = outptr;
+     }
+   grub_memcpy (iv_in, iv, cipher->cipher->blocksize);
+   return GPG_ERR_NO_ERROR;
+ }
+ gcry_err_code_t
+ grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher,
+                        void *out, void *in, grub_size_t size,
+                        void *iv)
+ {
+   grub_uint8_t *inptr, *outptr, *end;
+   grub_uint8_t ivt[cipher->cipher->blocksize];
+   if (!cipher->cipher->decrypt)
+     return GPG_ERR_NOT_SUPPORTED;
+   if (size % cipher->cipher->blocksize != 0)
+     return GPG_ERR_INV_ARG;
+   end = (grub_uint8_t *) in + size;
+   for (inptr = in, outptr = out; inptr < end;
+        inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize)
+     {
+       grub_memcpy (ivt, inptr, cipher->cipher->blocksize);
+       cipher->cipher->decrypt (cipher->ctx, outptr, inptr);
+       grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize);
+       grub_memcpy (iv, ivt, cipher->cipher->blocksize);
+     }
+   return GPG_ERR_NO_ERROR;
+ }
+ /* Based on gcry/cipher/md.c.  */
+ struct grub_crypto_hmac_handle *
+ grub_crypto_hmac_init (const struct gcry_md_spec *md,
+                      const void *key, grub_size_t keylen)
+ {
+   grub_uint8_t *helpkey = NULL;
+   grub_uint8_t *ipad = NULL, *opad = NULL;
+   void *ctx = NULL;
+   struct grub_crypto_hmac_handle *ret = NULL;
+   unsigned i;
+   if (md->mdlen > md->blocksize)
+     return NULL;
+   ctx = grub_malloc (md->contextsize);
+   if (!ctx)
+     goto err;
+   if ( keylen > md->blocksize ) 
+     {
+       helpkey = grub_malloc (md->mdlen);
+       if (!helpkey)
+       goto err;
+       grub_crypto_hash (md, helpkey, key, keylen);
+       key = helpkey;
+       keylen = md->mdlen;
+     }
+   ipad = grub_zalloc (md->blocksize);
+   if (!ipad)
+     goto err;
+   opad = grub_zalloc (md->blocksize);
+   if (!opad)
+     goto err;
+   grub_memcpy ( ipad, key, keylen );
+   grub_memcpy ( opad, key, keylen );
+   for (i=0; i < md->blocksize; i++ ) 
+     {
+       ipad[i] ^= 0x36;
+       opad[i] ^= 0x5c;
+     }
+   grub_free (helpkey);
+   helpkey = NULL;
+   md->init (ctx);
+   md->write (ctx, ipad, md->blocksize); /* inner pad */
+   grub_memset (ipad, 0, md->blocksize);
+   grub_free (ipad);
+   ipad = NULL;
+   ret = grub_malloc (sizeof (*ret));
+   if (!ret)
+     goto err;
+   ret->md = md;
+   ret->ctx = ctx;
+   ret->opad = opad;
+   return ret;
+  err:
+   grub_free (helpkey);
+   grub_free (ctx);
+   grub_free (ipad);
+   grub_free (opad);
+   return NULL;
+ }
+ void
+ grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data,
+                       grub_size_t datalen)
+ {
+   hnd->md->write (hnd->ctx, data, datalen);
+ }
+ gcry_err_code_t
+ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out)
+ {
+   grub_uint8_t *p;
+   grub_uint8_t *ctx2;
+   ctx2 = grub_malloc (hnd->md->contextsize);
+   if (!ctx2)
+     return GPG_ERR_OUT_OF_MEMORY;
+   hnd->md->final (hnd->ctx);
+   hnd->md->read (hnd->ctx);
+   p = hnd->md->read (hnd->ctx);
+   hnd->md->init (ctx2);
+   hnd->md->write (ctx2, hnd->opad, hnd->md->blocksize);
+   hnd->md->write (ctx2, p, hnd->md->mdlen);
+   hnd->md->final (ctx2);
+   grub_memset (hnd->opad, 0, hnd->md->blocksize);
+   grub_free (hnd->opad);
+   grub_memset (hnd->ctx, 0, hnd->md->contextsize);
+   grub_free (hnd->ctx);
+   grub_memcpy (out, hnd->md->read (ctx2), hnd->md->mdlen);
+   grub_memset (ctx2, 0, hnd->md->contextsize);
+   grub_free (ctx2);
+   grub_memset (hnd, 0, sizeof (*hnd));
+   grub_free (hnd);
+   return GPG_ERR_NO_ERROR;
+ }
+ gcry_err_code_t
+ grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
+                        const void *key, grub_size_t keylen,
+                        void *data, grub_size_t datalen, void *out)
+ {
+   struct grub_crypto_hmac_handle *hnd;
+   hnd = grub_crypto_hmac_init (md, key, keylen);
+   if (!hnd)
+     return GPG_ERR_OUT_OF_MEMORY;
+   grub_crypto_hmac_write (hnd, data, datalen);
+   return grub_crypto_hmac_fini (hnd, out);
+ }
+ grub_err_t
+ grub_crypto_gcry_error (gcry_err_code_t in)
+ {
+   if (in == GPG_ERR_NO_ERROR)
+     return GRUB_ERR_NONE;
+   return GRUB_ACCESS_DENIED;
+ }
+ int
+ grub_crypto_memcmp (const void *a, const void *b, grub_size_t n)
+ {
+   register grub_size_t counter = 0;
+   const grub_uint8_t *pa, *pb;
+   for (pa = a, pb = b; n; pa++, pb++, n--)
+     {
+       if (*pa != *pb)
+       counter++;
+     }
+   return !!counter;
+ }
+ #ifndef GRUB_MKPASSWD
+ int
+ grub_password_get (char buf[], unsigned buf_size)
+ {
+   unsigned cur_len = 0;
+   int key;
+   while (1)
+     {
++      key = grub_getkey (); 
+       if (key == '\n' || key == '\r')
+       break;
+       if (key == '\e')
+       {
+         cur_len = 0;
+         break;
+       }
+       if (key == '\b')
+       {
+         cur_len--;
+         continue;
+       }
+       if (!grub_isprint (key))
+       continue;
+       if (cur_len + 2 < buf_size)
+       buf[cur_len++] = key;
+     }
+   grub_memset (buf + cur_len, 0, buf_size - cur_len);
+   grub_xputs ("\n");
+   grub_refresh ();
+   return (key != '\e');
+ }
+ #endif
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25aee3a8051be06101e64ff611fab3ab3a99fdaf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008, 2009 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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/symbol.h>
++
++      .p2align        2
++
++
++      .code32
++
++/*
++ * Use cdecl calling convention for *BSD kernels.
++ */
++
++FUNCTION(grub_unix_real_boot)
++
++      /* Interrupts should be disabled.  */
++        cli
++
++      /* Discard `grub_unix_real_boot' return address.  */
++        popl    %eax
++
++        /* Fetch `entry' address ...  */
++        popl  %eax
++
++        /*
++         * ... and put our return address in its place. The kernel will
++         * ignore it, but it expects %esp to point to it.
++         */
++        call  *%eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a568fff4d25fbd292aac64b1d7aa48668d90021d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,124 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (c) 2003  Peter Wemm <peter@FreeBSD.org>
++ *  Copyright (C) 2009  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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++/* Based on the code from FreeBSD originally distributed under the
++   following terms: */
++
++/*-
++ * Copyright (c) 2003  Peter Wemm <peter@FreeBSD.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * $FreeBSD$
++ */
++
++
++#define MSR_EFER      0xc0000080
++#define EFER_LME      0x00000100
++#define CR4_PAE               0x00000020
++#define CR4_PSE               0x00000010
++#define CR0_PG                0x80000000
++
++#include <grub/symbol.h>
++
++      .p2align        2
++
++      .code32
++
++
++VARIABLE(grub_bsd64_trampoline_start)
++
++      /* Discard `grub_unix_real_boot' return address.  */
++        popl    %eax
++
++        /* entry  */
++        popl  %edi
++
++        /* entry_hi  */
++        popl  %esi
++
++      cli
++
++      /* Turn on EFER.LME.  */
++      movl    $MSR_EFER, %ecx
++      rdmsr
++      orl     $EFER_LME, %eax
++        wrmsr
++
++      /* Turn on PAE.  */
++      movl    %cr4, %eax
++      orl     $(CR4_PAE | CR4_PSE), %eax
++      movl    %eax, %cr4
++
++      /* Set %cr3 for PT4.  */
++      popl    %eax
++      movl    %eax, %cr3
++
++      /* Push a dummy return address.  */
++      pushl   %eax
++
++      /* Turn on paging (implicitly sets EFER.LMA).  */
++      movl    %cr0, %eax
++      orl     $CR0_PG, %eax
++      movl    %eax, %cr0
++
++      /* Now we're in compatibility mode. set %cs for long mode.  */
++      /* lgdt */
++      .byte 0x0f
++      .byte 0x01
++      .byte 0x15
++VARIABLE (grub_bsd64_trampoline_gdt)
++      .long 0x0
++
++      /* ljmp */
++      .byte 0xea
++VARIABLE (grub_bsd64_trampoline_selfjump)
++      .long 0x0
++      .word 0x08
++
++      .code64
++
++bsd64_longmode:
++         /* We're still running V=P, jump to entry point.  */
++      movl    %esi, %eax
++      salq    $32, %rax
++      orq     %rdi, %rax
++      pushq   %rax
++      ret
++VARIABLE(grub_bsd64_trampoline_end)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d37feb983364a18b412757a22637cf92defc3d8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,945 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2006,2007,2008,2009,2010  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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/machine/loader.h>
++#include <grub/file.h>
++#include <grub/disk.h>
++#include <grub/err.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/cpu/linux.h>
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/command.h>
++#include <grub/memory.h>
++#include <grub/env.h>
++#include <grub/video.h>
++#include <grub/time.h>
++#include <grub/i18n.h>
++
++#define GRUB_LINUX_CL_OFFSET          0x1000
++#define GRUB_LINUX_CL_END_OFFSET      0x2000
++
++#define NEXT_MEMORY_DESCRIPTOR(desc, size)      \
++  ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
++
++static grub_dl_t my_mod;
++
++static grub_size_t linux_mem_size;
++static int loaded;
++static void *real_mode_mem;
++static void *prot_mode_mem;
++static void *initrd_mem;
++static grub_efi_uintn_t real_mode_pages;
++static grub_efi_uintn_t prot_mode_pages;
++static grub_efi_uintn_t initrd_pages;
++static void *mmap_buf;
++
++static grub_uint8_t gdt[] __attribute__ ((aligned(16))) =
++  {
++    /* NULL.  */
++    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++    /* Reserved.  */
++    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++    /* Code segment.  */
++    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
++    /* Data segment.  */
++    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
++  };
++
++struct gdt_descriptor
++{
++  grub_uint16_t limit;
++  void *base;
++} __attribute__ ((packed));
++
++static struct gdt_descriptor gdt_desc =
++  {
++    sizeof (gdt) - 1,
++    gdt
++  };
++
++struct idt_descriptor
++{
++  grub_uint16_t limit;
++  void *base;
++} __attribute__ ((packed));
++
++static struct idt_descriptor idt_desc =
++  {
++    0,
++    0
++  };
++
++static inline grub_size_t
++page_align (grub_size_t size)
++{
++  return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
++}
++
++/* Find the optimal number of pages for the memory map. Is it better to
++   move this code to efi/mm.c?  */
++static grub_efi_uintn_t
++find_mmap_size (void)
++{
++  static grub_efi_uintn_t mmap_size = 0;
++
++  if (mmap_size != 0)
++    return mmap_size;
++
++  mmap_size = (1 << 12);
++  while (1)
++    {
++      int ret;
++      grub_efi_memory_descriptor_t *mmap;
++      grub_efi_uintn_t desc_size;
++
++      mmap = grub_malloc (mmap_size);
++      if (! mmap)
++      return 0;
++
++      ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
++      grub_free (mmap);
++
++      if (ret < 0)
++      grub_fatal ("cannot get memory map");
++      else if (ret > 0)
++      break;
++
++      mmap_size += (1 << 12);
++    }
++
++  /* Increase the size a bit for safety, because GRUB allocates more on
++     later, and EFI itself may allocate more.  */
++  mmap_size += (1 << 12);
++
++  return page_align (mmap_size);
++}
++
++static void
++free_pages (void)
++{
++  if (real_mode_mem)
++    {
++      grub_efi_free_pages ((grub_addr_t) real_mode_mem, real_mode_pages);
++      real_mode_mem = 0;
++    }
++
++  if (prot_mode_mem)
++    {
++      grub_efi_free_pages ((grub_addr_t) prot_mode_mem, prot_mode_pages);
++      prot_mode_mem = 0;
++    }
++
++  if (initrd_mem)
++    {
++      grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
++      initrd_mem = 0;
++    }
++}
++
++/* Allocate pages for the real mode code and the protected mode code
++   for linux as well as a memory map buffer.  */
++static int
++allocate_pages (grub_size_t prot_size)
++{
++  grub_efi_uintn_t desc_size;
++  grub_efi_memory_descriptor_t *mmap, *mmap_end;
++  grub_efi_uintn_t mmap_size, tmp_mmap_size;
++  grub_efi_memory_descriptor_t *desc;
++  grub_size_t real_size;
++
++  /* Make sure that each size is aligned to a page boundary.  */
++  real_size = GRUB_LINUX_CL_END_OFFSET;
++  prot_size = page_align (prot_size);
++  mmap_size = find_mmap_size ();
++
++  grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
++              (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
++
++  /* Calculate the number of pages; Combine the real mode code with
++     the memory map buffer for simplicity.  */
++  real_mode_pages = ((real_size + mmap_size) >> 12);
++  prot_mode_pages = (prot_size >> 12);
++
++  /* Initialize the memory pointers with NULL for convenience.  */
++  real_mode_mem = 0;
++  prot_mode_mem = 0;
++
++  /* Read the memory map temporarily, to find free space.  */
++  mmap = grub_malloc (mmap_size);
++  if (! mmap)
++    return 0;
++
++  tmp_mmap_size = mmap_size;
++  if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
++    grub_fatal ("cannot get memory map");
++
++  mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
++
++  /* First, find free pages for the real mode code
++     and the memory map buffer.  */
++  for (desc = mmap;
++       desc < mmap_end;
++       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
++    {
++      /* Probably it is better to put the real mode code in the traditional
++       space for safety.  */
++      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
++        && desc->physical_start <= 0x90000
++        && desc->num_pages >= real_mode_pages)
++      {
++        grub_efi_physical_address_t physical_end;
++        grub_efi_physical_address_t addr;
++
++        physical_end = desc->physical_start + (desc->num_pages << 12);
++        if (physical_end > 0x90000)
++          physical_end = 0x90000;
++
++        grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n",
++                      (unsigned) desc->physical_start,
++                      (unsigned) physical_end);
++        addr = physical_end - real_size - mmap_size;
++        if (addr < 0x10000)
++          continue;
++
++        grub_dprintf ("linux", "trying to allocate %u pages at %lx\n",
++                      (unsigned) real_mode_pages, (unsigned long) addr);
++        real_mode_mem = grub_efi_allocate_pages (addr, real_mode_pages);
++        if (! real_mode_mem)
++          grub_fatal ("cannot allocate pages");
++
++        desc->num_pages -= real_mode_pages;
++        break;
++      }
++    }
++
++  if (! real_mode_mem)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
++      goto fail;
++    }
++
++  mmap_buf = (void *) ((char *) real_mode_mem + real_size);
++
++  /* Next, find free pages for the protected mode code.  */
++  /* XXX what happens if anything is using this address?  */
++  prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages + 1);
++  if (! prot_mode_mem)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY,
++                "cannot allocate protected mode pages");
++      goto fail;
++    }
++
++  grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
++              "prot_mode_mem = %lx, prot_mode_pages = %x\n",
++              (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
++              (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
++
++  grub_free (mmap);
++  return 1;
++
++ fail:
++  grub_free (mmap);
++  free_pages ();
++  return 0;
++}
++
++static void
++grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
++                    grub_uint64_t start, grub_uint64_t size,
++                    grub_uint32_t type)
++{
++  int n = *e820_num;
++
++  if (n >= GRUB_E820_MAX_ENTRY)
++    grub_fatal ("Too many e820 memory map entries");
++
++  if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
++      (e820_map[n - 1].type == type))
++      e820_map[n - 1].size += size;
++  else
++    {
++      e820_map[n].addr = start;
++      e820_map[n].size = size;
++      e820_map[n].type = type;
++      (*e820_num)++;
++    }
++}
++
++static grub_err_t
++grub_linux_setup_video (struct linux_kernel_params *params)
++{
++  struct grub_video_mode_info mode_info;
++  void *framebuffer;
++  grub_err_t err;
++
++  err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
++
++  if (err)
++    return err;
++
++  params->lfb_width = mode_info.width;
++  params->lfb_height = mode_info.height;
++  params->lfb_depth = mode_info.bpp;
++  params->lfb_line_len = mode_info.pitch;
++
++  params->lfb_base = (grub_size_t) framebuffer;
++  params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height,
++                             65536);
++
++  params->red_mask_size = mode_info.red_mask_size;
++  params->red_field_pos = mode_info.red_field_pos;
++  params->green_mask_size = mode_info.green_mask_size;
++  params->green_field_pos = mode_info.green_field_pos;
++  params->blue_mask_size = mode_info.blue_mask_size;
++  params->blue_field_pos = mode_info.blue_field_pos;
++  params->reserved_mask_size = mode_info.reserved_mask_size;
++  params->reserved_field_pos = mode_info.reserved_field_pos;
++
++  params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
++
++#ifdef GRUB_MACHINE_PCBIOS
++  /* VESA packed modes may come with zeroed mask sizes, which need
++     to be set here according to DAC Palette width.  If we don't,
++     this results in Linux displaying a black screen.  */
++  if (mode_info.bpp <= 8)
++    {
++      struct grub_vbe_info_block controller_info;
++      int status;
++      int width = 8;
++
++      status = grub_vbe_bios_get_controller_info (&controller_info);
++
++      if (status == GRUB_VBE_STATUS_OK &&
++        (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
++      status = grub_vbe_bios_set_dac_palette_width (&width);
++
++      if (status != GRUB_VBE_STATUS_OK)
++      /* 6 is default after mode reset.  */
++      width = 6;
++
++      params->red_mask_size = params->green_mask_size
++      = params->blue_mask_size = width;
++      params->reserved_mask_size = 0;
++    }
++#endif
++
++  return 0;
++}
++
++#ifdef __x86_64__
++extern grub_uint8_t grub_linux_trampoline_start[];
++extern grub_uint8_t grub_linux_trampoline_end[];
++#endif
++
++static grub_err_t
++grub_linux_boot (void)
++{
++  struct linux_kernel_params *params;
++  grub_efi_uintn_t mmap_size;
++  grub_efi_uintn_t map_key;
++  grub_efi_uintn_t desc_size;
++  grub_efi_uint32_t desc_version;
++  int e820_num;
++  const char *modevar;
++  char *tmp;
++  grub_err_t err;
++
++  params = real_mode_mem;
++
++  grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
++              (unsigned) params->code32_start,
++              (unsigned long) &(idt_desc.limit),
++              (unsigned long) &(gdt_desc.limit));
++  grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
++              (unsigned) idt_desc.limit, (unsigned long) idt_desc.base,
++              (unsigned) gdt_desc.limit, (unsigned long) gdt_desc.base);
++
++  auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
++  int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
++    {
++      switch (type)
++        {
++        case GRUB_MACHINE_MEMORY_AVAILABLE:
++        grub_e820_add_region (params->e820_map, &e820_num,
++                              addr, size, GRUB_E820_RAM);
++        break;
++
++#ifdef GRUB_MACHINE_MEMORY_ACPI
++        case GRUB_MACHINE_MEMORY_ACPI:
++        grub_e820_add_region (params->e820_map, &e820_num,
++                              addr, size, GRUB_E820_ACPI);
++        break;
++#endif
++
++#ifdef GRUB_MACHINE_MEMORY_NVS
++        case GRUB_MACHINE_MEMORY_NVS:
++        grub_e820_add_region (params->e820_map, &e820_num,
++                              addr, size, GRUB_E820_NVS);
++        break;
++#endif
++
++#ifdef GRUB_MACHINE_MEMORY_CODE
++        case GRUB_MACHINE_MEMORY_CODE:
++        grub_e820_add_region (params->e820_map, &e820_num,
++                              addr, size, GRUB_E820_EXEC_CODE);
++        break;
++#endif
++
++        default:
++          grub_e820_add_region (params->e820_map, &e820_num,
++                                addr, size, GRUB_E820_RESERVED);
++        }
++      return 0;
++    }
++
++  e820_num = 0;
++  grub_mmap_iterate (hook);
++  params->mmap_size = e820_num;
++
++  grub_dprintf ("linux", "Trampoline at %p. code32=%x, real_mode_mem=%p\n",
++              ((char *) prot_mode_mem + (prot_mode_pages << 12)),
++              (unsigned) params->code32_start, real_mode_mem);
++
++  modevar = grub_env_get ("gfxpayload");
++
++  /* Now all graphical modes are acceptable.
++     May change in future if we have modes without framebuffer.  */
++  if (modevar && *modevar != 0)
++    {
++      tmp = grub_xasprintf ("%s;auto", modevar);
++      if (! tmp)
++      return grub_errno;
++      err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
++      grub_free (tmp);
++    }
++  else
++    err = grub_video_set_mode ("auto", GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
++
++  if (!err)
++    err = grub_linux_setup_video (params);
++
++  if (err)
++    {
++      grub_print_error ();
++      grub_printf ("Booting however\n");
++      grub_errno = GRUB_ERR_NONE;
++    }
++
++  mmap_size = find_mmap_size ();
++  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
++                             &desc_size, &desc_version) <= 0)
++    grub_fatal ("cannot get memory map");
++
++  if (! grub_efi_exit_boot_services (map_key))
++     grub_fatal ("cannot exit boot services");
++
++  /* Note that no boot services are available from here.  */
++
++  /* Pass EFI parameters.  */
++  if (grub_le_to_cpu16 (params->version) >= 0x0206)
++    {
++      params->v0206.efi_mem_desc_size = desc_size;
++      params->v0206.efi_mem_desc_version = desc_version;
++      params->v0206.efi_mmap = (grub_uint32_t) (unsigned long) mmap_buf;
++      params->v0206.efi_mmap_size = mmap_size;
++#ifdef __x86_64__
++      params->v0206.efi_mmap_hi = (grub_uint32_t) ((grub_uint64_t) mmap_buf >> 32);
++#endif
++    }
++  else if (grub_le_to_cpu16 (params->version) >= 0x0204)
++    {
++      params->v0204.efi_mem_desc_size = desc_size;
++      params->v0204.efi_mem_desc_version = desc_version;
++      params->v0204.efi_mmap = (grub_uint32_t) (unsigned long) mmap_buf;
++      params->v0204.efi_mmap_size = mmap_size;
++    }
++
++#ifdef __x86_64__
++
++  grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12),
++             grub_linux_trampoline_start,
++             grub_linux_trampoline_end - grub_linux_trampoline_start);
++
++  ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem
++                                     + (prot_mode_pages << 12)))
++    (params->code32_start, real_mode_mem);
++
++#else
++
++  /* Hardware interrupts are not safe any longer.  */
++  asm volatile ("cli" : : );
++
++  /* Load the IDT and the GDT for the bootstrap.  */
++  asm volatile ("lidt %0" : : "m" (idt_desc));
++  asm volatile ("lgdt %0" : : "m" (gdt_desc));
++
++  /* Pass parameters.  */
++  asm volatile ("movl %0, %%ecx" : : "m" (params->code32_start));
++  asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem));
++
++  asm volatile ("xorl %%ebx, %%ebx" : : );
++
++  /* Enter Linux.  */
++  asm volatile ("jmp *%%ecx" : : );
++
++#endif
++
++  /* Never reach here.  */
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_linux_unload (void)
++{
++  free_pages ();
++  grub_dl_unref (my_mod);
++  loaded = 0;
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
++              int argc, char *argv[])
++{
++  grub_file_t file = 0;
++  struct linux_kernel_header lh;
++  struct linux_kernel_params *params;
++  grub_uint8_t setup_sects;
++  grub_size_t real_size, prot_size;
++  grub_ssize_t len;
++  int i;
++  char *dest;
++
++  grub_dl_ref (my_mod);
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
++      goto fail;
++    }
++
++  file = grub_file_open (argv[0]);
++  if (! file)
++    goto fail;
++
++  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++    {
++      grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
++      goto fail;
++    }
++
++  if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
++    {
++      grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
++      goto fail;
++    }
++
++  if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
++    {
++      grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
++      goto fail;
++    }
++
++  /* EFI support is quite new, so reject old versions.  */
++  if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
++      || grub_le_to_cpu16 (lh.version) < 0x0203)
++    {
++      grub_error (GRUB_ERR_BAD_OS, "too old version");
++      goto fail;
++    }
++
++  /* I'm not sure how to support zImage on EFI.  */
++  if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
++    {
++      grub_error (GRUB_ERR_BAD_OS, "zImage is not supported");
++      goto fail;
++    }
++
++  setup_sects = lh.setup_sects;
++
++  /* If SETUP_SECTS is not set, set it to the default (4).  */
++  if (! setup_sects)
++    setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
++
++  real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
++  prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
++
++  if (! allocate_pages (prot_size))
++    goto fail;
++
++  params = (struct linux_kernel_params *) real_mode_mem;
++  grub_memset (params, 0, GRUB_LINUX_CL_END_OFFSET);
++  grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
++
++  params->ps_mouse = params->padding10 =  0;
++
++  len = 0x400 - sizeof (lh);
++  if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
++    {
++      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++      goto fail;
++    }
++
++  params->type_of_loader = (LINUX_LOADER_ID_GRUB << 4);
++
++  params->cl_magic = GRUB_LINUX_CL_MAGIC;
++  params->cl_offset = 0x1000;
++  params->cmd_line_ptr = (unsigned long) real_mode_mem + 0x1000;
++  params->ramdisk_image = 0;
++  params->ramdisk_size = 0;
++
++  params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
++  params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
++
++  /* These are not needed to be precise, because Linux uses these values
++     only to raise an error when the decompression code cannot find good
++     space.  */
++  params->ext_mem = ((32 * 0x100000) >> 10);
++  params->alt_mem = ((32 * 0x100000) >> 10);
++
++  {
++    grub_term_output_t term;
++    int found = 0;
++    FOR_ACTIVE_TERM_OUTPUTS(term)
++      if (grub_strcmp (term->name, "vga_text") == 0
++        || grub_strcmp (term->name, "console") == 0)
++      {
++        grub_uint16_t pos = grub_term_getxy (term);
++        params->video_cursor_x = pos >> 8;
++        params->video_cursor_y = pos & 0xff;
++        params->video_width = grub_term_width (term);
++        params->video_height = grub_term_height (term);
++        found = 1;
++        break;
++      }
++    if (!found)
++      {
++      params->video_cursor_x = 0;
++      params->video_cursor_y = 0;
++      params->video_width = 80;
++      params->video_height = 25;
++      }
++  }
++  params->video_page = 0; /* ??? */
++  params->video_mode = grub_efi_system_table->con_out->mode->mode;
++  params->video_ega_bx = 0;
++  params->have_vga = 0;
++  params->font_size = 16; /* XXX */
++
++  if (grub_le_to_cpu16 (params->version) >= 0x0206)
++    {
++      params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
++      params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
++#ifdef __x86_64__
++      params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
++#endif
++    }
++  else if (grub_le_to_cpu16 (params->version) >= 0x0204)
++    {
++      params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
++      params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
++    }
++
++#if 0
++  /* The structure is zeroed already.  */
++
++  /* No VBE on EFI.  */
++  params->lfb_width = 0;
++  params->lfb_height = 0;
++  params->lfb_depth = 0;
++  params->lfb_base = 0;
++  params->lfb_size = 0;
++  params->lfb_line_len = 0;
++  params->red_mask_size = 0;
++  params->red_field_pos = 0;
++  params->green_mask_size = 0;
++  params->green_field_pos = 0;
++  params->blue_mask_size = 0;
++  params->blue_field_pos = 0;
++  params->reserved_mask_size = 0;
++  params->reserved_field_pos = 0;
++  params->vesapm_segment = 0;
++  params->vesapm_offset = 0;
++  params->lfb_pages = 0;
++  params->vesa_attrib = 0;
++
++  /* No APM on EFI.  */
++  params->apm_version = 0;
++  params->apm_code_segment = 0;
++  params->apm_entry = 0;
++  params->apm_16bit_code_segment = 0;
++  params->apm_data_segment = 0;
++  params->apm_flags = 0;
++  params->apm_code_len = 0;
++  params->apm_data_len = 0;
++
++  /* XXX is there any way to use SpeedStep on EFI?  */
++  params->ist_signature = 0;
++  params->ist_command = 0;
++  params->ist_event = 0;
++  params->ist_perf_level = 0;
++
++  /* Let the kernel probe the information.  */
++  grub_memset (params->hd0_drive_info, 0, sizeof (params->hd0_drive_info));
++  grub_memset (params->hd1_drive_info, 0, sizeof (params->hd1_drive_info));
++
++  /* No MCA on EFI.  */
++  params->rom_config_len = 0;
++
++  /* No need to fake the BIOS's memory map.  */
++  params->mmap_size = 0;
++
++  /* Let the kernel probe the information.  */
++  params->ps_mouse = 0;
++
++  /* Clear padding for future compatibility.  */
++  grub_memset (params->padding1, 0, sizeof (params->padding1));
++  grub_memset (params->padding2, 0, sizeof (params->padding2));
++  grub_memset (params->padding3, 0, sizeof (params->padding3));
++  grub_memset (params->padding4, 0, sizeof (params->padding4));
++  grub_memset (params->padding5, 0, sizeof (params->padding5));
++  grub_memset (params->padding6, 0, sizeof (params->padding6));
++  grub_memset (params->padding7, 0, sizeof (params->padding7));
++  grub_memset (params->padding8, 0, sizeof (params->padding8));
++  grub_memset (params->padding9, 0, sizeof (params->padding9));
++
++#endif
++
++  /* The other EFI parameters are filled when booting.  */
++
++  grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
++
++  /* XXX there is no way to know if the kernel really supports EFI.  */
++  grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
++              (unsigned) real_size, (unsigned) prot_size);
++
++  /* Detect explicitly specified memory size, if any.  */
++  linux_mem_size = 0;
++  for (i = 1; i < argc; i++)
++    if (grub_memcmp (argv[i], "mem=", 4) == 0)
++      {
++      char *val = argv[i] + 4;
++
++      linux_mem_size = grub_strtoul (val, &val, 0);
++
++      if (grub_errno)
++        {
++          grub_errno = GRUB_ERR_NONE;
++          linux_mem_size = 0;
++        }
++      else
++        {
++          int shift = 0;
++
++          switch (grub_tolower (val[0]))
++            {
++            case 'g':
++              shift += 10;
++            case 'm':
++              shift += 10;
++            case 'k':
++              shift += 10;
++            default:
++              break;
++            }
++
++          /* Check an overflow.  */
++          if (linux_mem_size > (~0UL >> shift))
++            linux_mem_size = 0;
++          else
++            linux_mem_size <<= shift;
++        }
++      }
++    else if (grub_memcmp (argv[i], "video=efifb", 11) == 0)
++      {
++      if (params->have_vga)
++        params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
++      }
++
++  /* Specify the boot file.  */
++  dest = grub_stpcpy ((char *) real_mode_mem + GRUB_LINUX_CL_OFFSET,
++                    "BOOT_IMAGE=");
++  dest = grub_stpcpy (dest, argv[0]);
++
++  /* Copy kernel parameters.  */
++  for (i = 1;
++       i < argc
++       && dest + grub_strlen (argv[i]) + 1 < ((char *) real_mode_mem
++                                              + GRUB_LINUX_CL_END_OFFSET);
++       i++)
++    {
++      *dest++ = ' ';
++      dest = grub_stpcpy (dest, argv[i]);
++    }
++
++  len = prot_size;
++  if (grub_file_read (file, (void *) GRUB_LINUX_BZIMAGE_ADDR, len) != len)
++    grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++
++  if (grub_errno == GRUB_ERR_NONE)
++    {
++      grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
++      loaded = 1;
++    }
++
++ fail:
++
++  if (file)
++    grub_file_close (file);
++
++  if (grub_errno != GRUB_ERR_NONE)
++    {
++      grub_dl_unref (my_mod);
++      loaded = 0;
++    }
++
++  return grub_errno;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
++               int argc, char *argv[])
++{
++  grub_file_t file = 0;
++  grub_ssize_t size;
++  grub_addr_t addr_min, addr_max;
++  grub_addr_t addr;
++  grub_efi_uintn_t mmap_size;
++  grub_efi_memory_descriptor_t *desc;
++  grub_efi_uintn_t desc_size;
++  struct linux_kernel_header *lh;
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
++      goto fail;
++    }
++
++  if (! loaded)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
++      goto fail;
++    }
++
++  file = grub_file_open (argv[0]);
++  if (! file)
++    goto fail;
++
++  size = grub_file_size (file);
++  initrd_pages = (page_align (size) >> 12);
++
++  lh = (struct linux_kernel_header *) real_mode_mem;
++
++  addr_max = (grub_cpu_to_le32 (lh->initrd_addr_max) << 10);
++  if (linux_mem_size != 0 && linux_mem_size < addr_max)
++    addr_max = linux_mem_size;
++
++  /* Linux 2.3.xx has a bug in the memory range check, so avoid
++     the last page.
++     Linux 2.2.xx has a bug in the memory range check, which is
++     worse than that of Linux 2.3.xx, so avoid the last 64kb.  */
++  addr_max -= 0x10000;
++
++  /* Usually, the compression ratio is about 50%.  */
++  addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
++           + page_align (size);
++
++  /* Find the highest address to put the initrd.  */
++  mmap_size = find_mmap_size ();
++  if (grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0) <= 0)
++    grub_fatal ("cannot get memory map");
++
++  addr = 0;
++  for (desc = mmap_buf;
++       desc < NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
++       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
++    {
++      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
++        && desc->num_pages >= initrd_pages)
++      {
++        grub_efi_physical_address_t physical_end;
++
++        physical_end = desc->physical_start + (desc->num_pages << 12);
++        if (physical_end > addr_max)
++          physical_end = addr_max;
++
++        if (physical_end < page_align (size))
++          continue;
++
++        physical_end -= page_align (size);
++
++        if ((physical_end >= addr_min) &&
++            (physical_end >= desc->physical_start) &&
++            (physical_end > addr))
++          addr = physical_end;
++      }
++    }
++
++  if (addr == 0)
++    {
++      grub_error (GRUB_ERR_OUT_OF_MEMORY, "no free pages available");
++      goto fail;
++    }
++
++  initrd_mem = grub_efi_allocate_pages (addr, initrd_pages);
++  if (! initrd_mem)
++    grub_fatal ("cannot allocate pages");
++
++  if (grub_file_read (file, initrd_mem, size) != size)
++    {
++      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++      goto fail;
++    }
++
++  grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
++              (unsigned) addr, (unsigned) size);
++
++  lh->ramdisk_image = addr;
++  lh->ramdisk_size = size;
++  lh->root_dev = 0x0100; /* XXX */
++
++ fail:
++  if (file)
++    grub_file_close (file);
++
++  return grub_errno;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++
++GRUB_MOD_INIT(linux)
++{
++  cmd_linux = grub_register_command ("linux", grub_cmd_linux,
++                                   0, N_("Load Linux."));
++  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
++                                    0, N_("Load initrd."));
++  my_mod = mod;
++}
++
++GRUB_MOD_FINI(linux)
++{
++  grub_unregister_command (cmd_linux);
++  grub_unregister_command (cmd_initrd);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f7556ffa4b22713fa0fc96932129d198971d093
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,311 @@@
++/* linux.c - boot Linux zImage or bzImage */
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/machine/loader.h>
++#include <grub/machine/memory.h>
++#include <grub/file.h>
++#include <grub/err.h>
++#include <grub/disk.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include <grub/mm.h>
++#include <grub/dl.h>
++#include <grub/env.h>
++#include <grub/term.h>
++#include <grub/cpu/linux.h>
++#include <grub/ieee1275/ieee1275.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++
++#define GRUB_OFW_LINUX_PARAMS_ADDR    0x90000
++#define GRUB_OFW_LINUX_KERNEL_ADDR    0x100000
++#define GRUB_OFW_LINUX_INITRD_ADDR    0x800000
++
++#define GRUB_OFW_LINUX_CL_OFFSET      0x1e00
++#define GRUB_OFW_LINUX_CL_LENGTH      0x100
++
++static grub_dl_t my_mod;
++
++static grub_size_t kernel_size;
++static char *kernel_addr, *kernel_cmdline;
++static grub_size_t initrd_size;
++
++static grub_err_t
++grub_linux_unload (void)
++{
++  grub_free (kernel_cmdline);
++  grub_free (kernel_addr);
++  kernel_cmdline = 0;
++  kernel_addr = 0;
++  initrd_size = 0;
++
++  grub_dl_unref (my_mod);
++
++  return GRUB_ERR_NONE;
++}
++
++/*
++static int
++grub_ieee1275_debug (void)
++{
++  struct enter_args
++  {
++    struct grub_ieee1275_common_hdr common;
++  }
++  args;
++
++  INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0);
++
++  if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
++    return -1;
++
++  return 0;
++}
++*/
++
++static grub_err_t
++grub_linux_boot (void)
++{
++  struct linux_kernel_params *params;
++  struct linux_kernel_header *lh;
++  char *prot_code;
++  char *bootpath;
++  grub_ssize_t len;
++
++  bootpath = grub_env_get ("root");
++  if (bootpath)
++    grub_ieee1275_set_property (grub_ieee1275_chosen,
++                                "bootpath", bootpath,
++                                grub_strlen (bootpath) + 1,
++                                &len);
++
++  params = (struct linux_kernel_params *) GRUB_OFW_LINUX_PARAMS_ADDR;
++  lh = (struct linux_kernel_header *) params;
++
++  grub_memset ((char *) params, 0, GRUB_OFW_LINUX_CL_OFFSET);
++
++  params->alt_mem = grub_mmap_get_upper () >> 10;
++  params->ext_mem = params->alt_mem;
++
++  lh->cmd_line_ptr = (char *)
++        (GRUB_OFW_LINUX_PARAMS_ADDR + GRUB_OFW_LINUX_CL_OFFSET);
++
++  params->cl_magic = GRUB_LINUX_CL_MAGIC;
++  params->cl_offset = GRUB_OFW_LINUX_CL_OFFSET;
++
++  {
++    grub_term_output_t term;
++    int found = 0;
++    FOR_ACTIVE_TERM_OUTPUTS(term)
++      if (grub_strcmp (term->name, "ofconsole") == 0)
++        {
++          grub_uint16_t pos = grub_term_getxy (term);
++          params->video_cursor_x = pos >> 8;
++          params->video_cursor_y = pos & 0xff;
++          params->video_width = grub_term_width (term);
++          params->video_height = grub_term_height (term);
++          found = 1;
++          break;
++        }
++    if (!found)
++      {
++        params->video_cursor_x = 0;
++        params->video_cursor_y = 0;
++        params->video_width = 80;
++        params->video_height = 25;
++      }
++  }
++
++  params->font_size = 16;
++
++  params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
++  params->ofw_num_items = 1;
++  params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
++  params->ofw_idt = 0;
++
++  if (initrd_size)
++    {
++      lh->type_of_loader = 1;
++      lh->ramdisk_image = GRUB_OFW_LINUX_INITRD_ADDR;
++      lh->ramdisk_size = initrd_size;
++    }
++
++  if (kernel_cmdline)
++    grub_strcpy (lh->cmd_line_ptr, kernel_cmdline);
++
++  prot_code = (char *) GRUB_OFW_LINUX_KERNEL_ADDR;
++  grub_memcpy (prot_code, kernel_addr, kernel_size);
++
++  asm volatile ("movl %0, %%esi" : : "m" (params));
++  asm volatile ("movl %%esi, %%esp" : : );
++  asm volatile ("movl %0, %%ecx" : : "m" (prot_code));
++  asm volatile ("xorl %%ebx, %%ebx" : : );
++  asm volatile ("jmp *%%ecx" : : );
++
++  return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
++              int argc, char *argv[])
++{
++  grub_file_t file = 0;
++  struct linux_kernel_header lh;
++  grub_uint8_t setup_sects;
++  grub_size_t real_size, prot_size;
++  int i;
++  char *dest;
++
++  grub_dl_ref (my_mod);
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
++      goto fail;
++    }
++
++  file = grub_file_open (argv[0]);
++  if (! file)
++    goto fail;
++
++  if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++    {
++      grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
++      goto fail;
++    }
++
++  if ((lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ||
++      (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)))
++    {
++      grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
++      goto fail;
++    }
++
++  setup_sects = lh.setup_sects;
++  if (! setup_sects)
++    setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
++
++  real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
++  prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
++
++  grub_dprintf ("linux", "Linux-%s, setup=0x%x, size=0x%x\n",
++              "bzImage", real_size, prot_size);
++
++  grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
++  if (grub_errno)
++    goto fail;
++
++  kernel_cmdline = grub_malloc (GRUB_OFW_LINUX_CL_LENGTH);
++  if (! kernel_cmdline)
++    goto fail;
++
++  dest = kernel_cmdline;
++  for (i = 1;
++       i < argc
++       && dest + grub_strlen (argv[i]) + 1 < (kernel_cmdline
++                                              + GRUB_OFW_LINUX_CL_LENGTH);
++       i++)
++    {
++      *dest++ = ' ';
++      dest = grub_stpcpy (dest, argv[i]);
++    }
++
++  kernel_addr = grub_malloc (prot_size);
++  if (! kernel_addr)
++    goto fail;
++
++  kernel_size = prot_size;
++  if (grub_file_read (file, kernel_addr, prot_size) != (int) prot_size)
++    grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++
++  if (grub_errno == GRUB_ERR_NONE)
++    grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
++
++fail:
++
++  if (file)
++    grub_file_close (file);
++
++  if (grub_errno != GRUB_ERR_NONE)
++    {
++      grub_free (kernel_cmdline);
++      grub_free (kernel_addr);
++      kernel_cmdline = 0;
++      kernel_addr = 0;
++
++      grub_dl_unref (my_mod);
++    }
++
++  return grub_errno;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
++               int argc, char *argv[])
++{
++  grub_file_t file = 0;
++
++  if (argc == 0)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
++      goto fail;
++    }
++
++  if (! kernel_addr)
++    {
++      grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
++      goto fail;
++    }
++
++  file = grub_file_open (argv[0]);
++  if (! file)
++    goto fail;
++
++  initrd_size = grub_file_size (file);
++  if (grub_file_read (file, (void *) GRUB_OFW_LINUX_INITRD_ADDR,
++                      initrd_size) != (int) initrd_size)
++    {
++      grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++      goto fail;
++    }
++
++fail:
++  if (file)
++    grub_file_close (file);
++
++  return grub_errno;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++
++GRUB_MOD_INIT(linux)
++{
++  cmd_linux = grub_register_command ("linux", grub_cmd_linux,
++                                   0, N_("Load Linux."));
++  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
++                                    0, N_("Load initrd."));
++  my_mod = mod;
++}
++
++GRUB_MOD_FINI(linux)
++{
++  grub_unregister_command (cmd_linux);
++  grub_unregister_command (cmd_initrd);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4acea7b1161a2cd415e976760f6815ef307f249a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,129 @@@
++/*
++ *  GRUB  --  GRand Unified Bootloader
++ *  Copyright (C) 2009  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 3 of the License, or
++ *  (at your option) any later version.
++ *
++ *  GRUB 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/symbol.h>
++
++
++      .p2align        4       /* force 16-byte alignment */
++VARIABLE(grub_linux_trampoline_start)
++      cli
++      /* %rdi contains protected memory start and %rsi
++      contains real memory start. */
++
++      mov %rsi, %rbx
++
++      call base
++base:
++      pop %rsi
++
++#ifdef APPLE_CC
++      lea (cont1 - base) (%esi, 1), %rax
++      mov %eax, (jump_vector - base) (%esi, 1)
++
++      lea (gdt - base) (%esi, 1), %rax
++      mov %rax, (gdtaddr - base) (%esi, 1)
++
++      /* Switch to compatibility mode. */
++
++      lidt (idtdesc - base) (%esi, 1)
++      lgdt (gdtdesc - base) (%esi, 1)
++
++      /* Update %cs. Thanks to David Miller for pointing this mistake out. */
++      ljmp *(jump_vector - base) (%esi, 1)
++#else
++      lea (cont1 - base) (%rsi, 1), %rax
++      mov %eax, (jump_vector - base) (%rsi, 1)
++
++      lea (gdt - base) (%rsi, 1), %rax
++      mov %rax, (gdtaddr - base) (%rsi, 1)
++
++      /* Switch to compatibility mode. */
++
++      lidt (idtdesc - base) (%rsi, 1)
++      lgdt (gdtdesc - base) (%rsi, 1)
++
++      /* Update %cs. Thanks to David Miller for pointing this mistake out. */
++      ljmp *(jump_vector - base) (%rsi, 1)
++#endif
++
++cont1:
++      .code32
++
++      /* Update other registers. */
++      mov $0x18, %eax
++      mov %eax, %ds
++      mov %eax, %es
++      mov %eax, %fs
++      mov %eax, %gs
++      mov %eax, %ss
++
++      /* Disable paging. */
++      mov %cr0, %eax
++      and $0x7fffffff, %eax
++      mov %eax, %cr0
++
++      /* Disable amd64. */
++      mov $0xc0000080, %ecx
++      rdmsr
++      and $0xfffffeff, %eax
++      wrmsr
++
++      /* Turn off PAE. */
++      movl %cr4, %eax
++      and $0xffffffcf, %eax
++      mov %eax, %cr4
++
++      jmp cont2
++cont2:
++      .code32
++
++      mov %ebx, %esi
++
++      jmp *%edi
++
++      /* GDT. */
++      .p2align 4
++gdt:
++      /* NULL.  */
++      .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++
++      /* Reserved.  */
++      .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++
++      /* Code segment.  */
++      .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00
++
++      /* Data segment.  */
++      .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
++
++gdtdesc:
++      .word 31
++gdtaddr:
++      .quad gdt
++
++idtdesc:
++      .word 0
++idtaddr:
++      .quad 0
++
++      .p2align 4
++jump_vector:
++      /* Jump location. Is filled by the code */
++      .long 0
++      .long 0x10
++VARIABLE(grub_linux_trampoline_end)
index 0000000000000000000000000000000000000000,bf1efbfdd904dc1d6345d4d7f0ed62bceeb191cd..e320289589ad2737e3f47afdbd0afff7a3ca1123
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,250 +1,250 @@@
 -      key = GRUB_TERM_ASCII_CHAR (grub_getkey ()); 
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/auth.h>
+ #include <grub/list.h>
+ #include <grub/mm.h>
+ #include <grub/misc.h>
+ #include <grub/env.h>
+ #include <grub/normal.h>
+ #include <grub/time.h>
+ #include <grub/i18n.h>
+ struct grub_auth_user
+ {
+   struct grub_auth_user *next;
+   char *name;
+   grub_auth_callback_t callback;
+   void *arg;
+   int authenticated;
+ };
+ struct grub_auth_user *users = NULL;
+ grub_err_t
+ grub_auth_register_authentication (const char *user,
+                                  grub_auth_callback_t callback,
+                                  void *arg)
+ {
+   struct grub_auth_user *cur;
+   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
+   if (!cur)
+     cur = grub_zalloc (sizeof (*cur));
+   if (!cur)
+     return grub_errno;
+   cur->callback = callback;
+   cur->arg = arg;
+   if (! cur->name)
+     {
+       cur->name = grub_strdup (user);
+       if (!cur->name)
+       {
+         grub_free (cur);
+         return grub_errno;
+       }
+       grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
+     }
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_auth_unregister_authentication (const char *user)
+ {
+   struct grub_auth_user *cur;
+   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
+   if (!cur)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
+   if (!cur->authenticated)
+     {
+       grub_free (cur->name);
+       grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
+       grub_free (cur);
+     }
+   else
+     {
+       cur->callback = NULL;
+       cur->arg = NULL;
+     }
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_auth_authenticate (const char *user)
+ {
+   struct grub_auth_user *cur;
+   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
+   if (!cur)
+     cur = grub_zalloc (sizeof (*cur));
+   if (!cur)
+     return grub_errno;
+   cur->authenticated = 1;
+   if (! cur->name)
+     {
+       cur->name = grub_strdup (user);
+       if (!cur->name)
+       {
+         grub_free (cur);
+         return grub_errno;
+       }
+       grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
+     }
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_auth_deauthenticate (const char *user)
+ {
+   struct grub_auth_user *cur;
+   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
+   if (!cur)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
+   if (!cur->callback)
+     {
+       grub_free (cur->name);
+       grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
+       grub_free (cur);
+     }
+   else
+     cur->authenticated = 0;
+   return GRUB_ERR_NONE;
+ }
+ static int
+ is_authenticated (const char *userlist)
+ {
+   const char *superusers;
+   struct grub_auth_user *user;
+   superusers = grub_env_get ("superusers");
+   if (!superusers)
+     return 1;
+   FOR_LIST_ELEMENTS (user, users)
+     {
+       if (!(user->authenticated))
+       continue;
+       if ((userlist && grub_strword (userlist, user->name))
+         || grub_strword (superusers, user->name))
+       return 1;
+     }
+   return 0;
+ }
+ static int
+ grub_username_get (char buf[], unsigned buf_size)
+ {
+   unsigned cur_len = 0;
+   int key;
+   while (1)
+     {
++      key = grub_getkey (); 
+       if (key == '\n' || key == '\r')
+       break;
+       if (key == '\e')
+       {
+         cur_len = 0;
+         break;
+       }
+       if (key == '\b')
+       {
+         cur_len--;
+         grub_printf ("\b");
+         continue;
+       }
+       if (!grub_isprint (key))
+       continue;
+       if (cur_len + 2 < buf_size)
+       {
+         buf[cur_len++] = key;
+         grub_printf ("%c", key);
+       }
+     }
+   grub_memset (buf + cur_len, 0, buf_size - cur_len);
+   grub_xputs ("\n");
+   grub_refresh ();
+   return (key != '\e');
+ }
+ grub_err_t
+ grub_auth_check_authentication (const char *userlist)
+ {
+   char login[1024];
+   struct grub_auth_user *cur = NULL;
+   grub_err_t err;
+   static unsigned long punishment_delay = 1;
+   char entered[GRUB_AUTH_MAX_PASSLEN];
+   struct grub_auth_user *user;
+   grub_memset (login, 0, sizeof (login));
+   if (is_authenticated (userlist))
+     {
+       punishment_delay = 1;
+       return GRUB_ERR_NONE;
+     }
+   grub_puts_ (N_("Enter username: "));
+   if (!grub_username_get (login, sizeof (login) - 1))
+     goto access_denied;
+   grub_puts_ (N_("Enter password: "));
+   if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN))
+     goto access_denied;
+   FOR_LIST_ELEMENTS (user, users)
+     {
+       if (grub_strcmp (login, user->name) == 0)
+       cur = user;
+     }
+   if (!cur || ! cur->callback)
+     goto access_denied;
+   err = cur->callback (login, entered, cur->arg);
+   if (is_authenticated (userlist))
+     {
+       punishment_delay = 1;
+       return GRUB_ERR_NONE;
+     }
+  access_denied:
+   grub_sleep (punishment_delay);
+   if (punishment_delay < GRUB_ULONG_MAX / 2)
+     punishment_delay *= 2;
+   return GRUB_ACCESS_DENIED;
+ }
index 0000000000000000000000000000000000000000,3647dcd3a617537a770e0c808f328d880704615a..b8c20d91cefacd3906cd1f3e736c901d5eb7baa6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,641 +1,650 @@@
 -  while ((key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && key != '\r')
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/normal.h>
+ #include <grub/misc.h>
+ #include <grub/term.h>
+ #include <grub/err.h>
+ #include <grub/types.h>
+ #include <grub/mm.h>
+ #include <grub/partition.h>
+ #include <grub/disk.h>
+ #include <grub/file.h>
+ #include <grub/env.h>
+ #include <grub/i18n.h>
+ #include <grub/charset.h>
+ static grub_uint32_t *kill_buf;
+ static int hist_size;
+ static grub_uint32_t **hist_lines = 0;
+ static int hist_pos = 0;
+ static int hist_end = 0;
+ static int hist_used = 0;
+ grub_err_t
+ grub_set_history (int newsize)
+ {
+   grub_uint32_t **old_hist_lines = hist_lines;
+   hist_lines = grub_malloc (sizeof (grub_uint32_t *) * newsize);
+   /* Copy the old lines into the new buffer.  */
+   if (old_hist_lines)
+     {
+       /* Remove the lines that don't fit in the new buffer.  */
+       if (newsize < hist_used)
+       {
+         int i;
+         int delsize = hist_used - newsize;
+         hist_used = newsize;
+         for (i = 1; i <= delsize; i++)
+           {
+             int pos = hist_end - i;
+             if (pos < 0)
+               pos += hist_size;
+             grub_free (old_hist_lines[pos]);
+           }
+         hist_end -= delsize;
+         if (hist_end < 0)
+           hist_end += hist_size;
+       }
+       if (hist_pos < hist_end)
+       grub_memmove (hist_lines, old_hist_lines + hist_pos,
+                     (hist_end - hist_pos) * sizeof (grub_uint32_t *));
+       else if (hist_used)
+       {
+         /* Copy the older part.  */
+         grub_memmove (hist_lines, old_hist_lines + hist_pos,
+                       (hist_size - hist_pos) * sizeof (grub_uint32_t *));
+         /* Copy the newer part. */
+         grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
+                       hist_end * sizeof (grub_uint32_t *));
+       }
+     }
+   grub_free (old_hist_lines);
+   hist_size = newsize;
+   hist_pos = 0;
+   hist_end = hist_used;
+   return 0;
+ }
+ /* Get the entry POS from the history where `0' is the newest
+    entry.  */
+ static grub_uint32_t *
+ grub_history_get (int pos)
+ {
+   pos = (hist_pos + pos) % hist_size;
+   return hist_lines[pos];
+ }
+ static grub_size_t
+ strlen_ucs4 (const grub_uint32_t *s)
+ {
+   const grub_uint32_t *p = s;
+   while (*p)
+     p++;
+   return p - s;
+ }
+ /* Replace the history entry on position POS with the string S.  */
+ static void
+ grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
+ {
+   grub_free (hist_lines[pos]);
+   hist_lines[pos] = grub_malloc ((len + 1) * sizeof (grub_uint32_t));
+   if (!hist_lines[pos])
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return ;
+     }
+   grub_memcpy (hist_lines[pos], s, len * sizeof (grub_uint32_t));
+   hist_lines[pos][len] = 0;
+ }
+ /* Insert a new history line S on the top of the history.  */
+ static void
+ grub_history_add (grub_uint32_t *s, grub_size_t len)
+ {
+   /* Remove the oldest entry in the history to make room for a new
+      entry.  */
+   if (hist_used + 1 > hist_size)
+     {
+       hist_end--;
+       if (hist_end < 0)
+       hist_end = hist_size + hist_end;
+       grub_free (hist_lines[hist_end]);
+     }
+   else
+     hist_used++;
+   /* Move to the next position.  */
+   hist_pos--;
+   if (hist_pos < 0)
+     hist_pos = hist_size + hist_pos;
+   /* Insert into history.  */
+   hist_lines[hist_pos] = NULL;
+   grub_history_set (hist_pos, s, len);
+ }
+ /* Replace the history entry on position POS with the string S.  */
+ static void
+ grub_history_replace (int pos, grub_uint32_t *s, grub_size_t len)
+ {
+   grub_history_set ((hist_pos + pos) % hist_size, s, len);
+ }
+ /* A completion hook to print items.  */
+ static void
+ print_completion (const char *item, grub_completion_type_t type, int count)
+ {
+   if (count == 0)
+     {
+       /* If this is the first time, print a label.  */
+       
+       grub_puts ("");
+       switch (type)
+       {
+       case GRUB_COMPLETION_TYPE_COMMAND:
+         grub_puts_ (N_("Possible commands are:"));
+         break;
+       case GRUB_COMPLETION_TYPE_DEVICE:
+         grub_puts_ (N_("Possible devices are:"));
+         break;
+       case GRUB_COMPLETION_TYPE_FILE:
+         grub_puts_ (N_("Possible files are:"));
+         break;
+       case GRUB_COMPLETION_TYPE_PARTITION:
+         grub_puts_ (N_("Possible partitions are:"));
+         break;
+       case GRUB_COMPLETION_TYPE_ARGUMENT:
+         grub_puts_ (N_("Possible arguments are:"));
+         break;
+       default:
+         grub_puts_ (N_("Possible things are:"));
+         break;
+       }
+       grub_puts ("");
+     }
+   if (type == GRUB_COMPLETION_TYPE_PARTITION)
+     {
+       grub_normal_print_device_info (item);
+       grub_errno = GRUB_ERR_NONE;
+     }
+   else
+     grub_printf (" %s", item);
+ }
+ struct cmdline_term
+ {
+   unsigned xpos, ypos, ystart, width, height;
+   struct grub_term_output *term;
+ };
+ /* Get a command-line. If ESC is pushed, return zero,
+    otherwise return command line.  */
+ /* FIXME: The dumb interface is not supported yet.  */
+ char *
+ grub_cmdline_get (const char *prompt)
+ {
+   grub_size_t lpos, llen;
+   grub_size_t plen;
+   grub_uint32_t *buf;
+   grub_size_t max_len = 256;
+   int key;
+   int histpos = 0;
+   auto void cl_insert (const grub_uint32_t *str);
+   auto void cl_delete (unsigned len);
+   auto inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos,
+                       grub_uint32_t c);
+   auto void cl_set_pos (struct cmdline_term *cl_term);
+   auto void cl_print_all (int pos, grub_uint32_t c);
+   auto void cl_set_pos_all (void);
+   auto void init_clterm (struct cmdline_term *cl_term_cur);
+   auto void init_clterm_all (void);
+   const char *prompt_translated = _(prompt);
+   struct cmdline_term *cl_terms;
+   char *ret;
+   unsigned nterms;
+   void cl_set_pos (struct cmdline_term *cl_term)
+   {
+     cl_term->xpos = (plen + lpos) % (cl_term->width - 1);
+     cl_term->ypos = cl_term->ystart + (plen + lpos) / (cl_term->width - 1);
+     grub_term_gotoxy (cl_term->term, cl_term->xpos, cl_term->ypos);
+   }
+   void cl_set_pos_all ()
+   {
+     unsigned i;
+     for (i = 0; i < nterms; i++)
+       cl_set_pos (&cl_terms[i]);
+   }
+   inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos, grub_uint32_t c)
+     {
+       grub_uint32_t *p;
+       for (p = buf + pos; p < buf + llen; p++)
+       {
+         if (c)
+           grub_putcode (c, cl_term->term);
+         else
+           grub_putcode (*p, cl_term->term);
+         cl_term->xpos++;
+         if (cl_term->xpos >= cl_term->width - 1)
+           {
+             cl_term->xpos = 0;
+             if (cl_term->ypos >= (unsigned) (cl_term->height - 1))
+               cl_term->ystart--;
+             else
+               cl_term->ypos++;
+             grub_putcode ('\n', cl_term->term);
+           }
+       }
+     }
+   void cl_print_all (int pos, grub_uint32_t c)
+   {
+     unsigned i;
+     for (i = 0; i < nterms; i++)
+       cl_print (&cl_terms[i], pos, c);
+   }
+   void cl_insert (const grub_uint32_t *str)
+     {
+       grub_size_t len = strlen_ucs4 (str);
+       if (len + llen >= max_len)
+       {
+         grub_uint32_t *nbuf;
+         max_len *= 2;
+         nbuf = grub_realloc (buf, sizeof (grub_uint32_t) * max_len);
+         if (nbuf)
+           buf = nbuf;
+         else
+           {
+             grub_print_error ();
+             grub_errno = GRUB_ERR_NONE;
+             max_len /= 2;
+           }
+       }
+       if (len + llen < max_len)
+       {
+         grub_memmove (buf + lpos + len, buf + lpos,
+                       (llen - lpos + 1) * sizeof (grub_uint32_t));
+         grub_memmove (buf + lpos, str, len * sizeof (grub_uint32_t));
+         llen += len;
+         cl_set_pos_all ();
+         cl_print_all (lpos, 0);
+         lpos += len;
+         cl_set_pos_all ();
+       }
+     }
+   void cl_delete (unsigned len)
+     {
+       if (lpos + len <= llen)
+       {
+         grub_size_t saved_lpos = lpos;
+         lpos = llen - len;
+         cl_set_pos_all ();
+         cl_print_all (lpos, ' ');
+         lpos = saved_lpos;
+         cl_set_pos_all ();
+         grub_memmove (buf + lpos, buf + lpos + len,
+                       sizeof (grub_uint32_t) * (llen - lpos + 1));
+         llen -= len;
+         cl_print_all (lpos, 0);
+         cl_set_pos_all ();
+       }
+     }
+   void init_clterm (struct cmdline_term *cl_term_cur)
+   {
+     cl_term_cur->xpos = plen;
+     cl_term_cur->ypos = (grub_term_getxy (cl_term_cur->term) & 0xFF);
+     cl_term_cur->ystart = cl_term_cur->ypos;
+     cl_term_cur->width = grub_term_width (cl_term_cur->term);
+     cl_term_cur->height = grub_term_height (cl_term_cur->term);
+   }
+   void init_clterm_all (void)
+   {
+     unsigned i;
+     for (i = 0; i < nterms; i++)
+       init_clterm (&cl_terms[i]);
+   }
+   buf = grub_malloc (max_len * sizeof (grub_uint32_t));
+   if (!buf)
+     return 0;
+   plen = grub_strlen (prompt_translated) + sizeof (" ") - 1;
+   lpos = llen = 0;
+   buf[0] = '\0';
+   {
+     grub_term_output_t term;
+     FOR_ACTIVE_TERM_OUTPUTS(term)
+       if ((grub_term_getxy (term) >> 8) != 0)
+       grub_putcode ('\n', term);
+   }
+   grub_printf ("%s ", prompt_translated);
+   grub_normal_reset_more ();
+   {
+     struct cmdline_term *cl_term_cur;
+     struct grub_term_output *cur;
+     nterms = 0;
+     FOR_ACTIVE_TERM_OUTPUTS(cur)
+       nterms++;
+     cl_terms = grub_malloc (sizeof (cl_terms[0]) * nterms);
+     if (!cl_terms)
+       return 0;
+     cl_term_cur = cl_terms;
+     FOR_ACTIVE_TERM_OUTPUTS(cur)
+     {
+       cl_term_cur->term = cur;
+       init_clterm (cl_term_cur);
+       cl_term_cur++;
+     }
+   }
+   if (hist_used == 0)
+     grub_history_add (buf, llen);
+   grub_refresh ();
 -      case 1: /* Ctrl-a */
++  while ((key = grub_getkey ()) != '\n' && key != '\r')
+     {
+       switch (key)
+       {
 -      case 2: /* Ctrl-b */
++      case GRUB_TERM_CTRL | 'a':
++      case GRUB_TERM_KEY_HOME:
+         lpos = 0;
+         cl_set_pos_all ();
+         break;
 -      case 5: /* Ctrl-e */
++      case GRUB_TERM_CTRL | 'b':
++      case GRUB_TERM_KEY_LEFT:
+         if (lpos > 0)
+           {
+             lpos--;
+             cl_set_pos_all ();
+           }
+         break;
 -      case 6: /* Ctrl-f */
++      case GRUB_TERM_CTRL | 'e':
++      case GRUB_TERM_KEY_END:
+         lpos = llen;
+         cl_set_pos_all ();
+         break;
 -      case 9: /* Ctrl-i or TAB */
++      case GRUB_TERM_CTRL | 'f':
++      case GRUB_TERM_KEY_RIGHT:
+         if (lpos < llen)
+           {
+             lpos++;
+             cl_set_pos_all ();
+           }
+         break;
 -      case 11:        /* Ctrl-k */
++      case GRUB_TERM_CTRL | 'i':
++      case '\t':
+         {
+           int restore;
+           char *insertu8;
+           char *bufu8;
+           grub_uint32_t c;
+           c = buf[lpos];
+           buf[lpos] = '\0';
+           bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos);
+           buf[lpos] = c;
+           if (!bufu8)
+             {
+               grub_print_error ();
+               grub_errno = GRUB_ERR_NONE;
+               break;
+             }
+           insertu8 = grub_normal_do_completion (bufu8, &restore,
+                                                 print_completion);
+           grub_free (bufu8);
+           grub_normal_reset_more ();
+           if (restore)
+             {
+               /* Restore the prompt.  */
+               grub_printf ("\n%s ", prompt_translated);
+               init_clterm_all ();
+               cl_print_all (0, 0);
+             }
+           if (insertu8)
+             {
+               grub_size_t insertlen;
+               grub_ssize_t t;
+               grub_uint32_t *insert;
+               insertlen = grub_strlen (insertu8);
+               insert = grub_malloc ((insertlen + 1) * sizeof (grub_uint32_t));
+               if (!insert)
+                 {
+                   grub_free (insertu8);
+                   grub_print_error ();
+                   grub_errno = GRUB_ERR_NONE;
+                   break;
+                 }
+               t = grub_utf8_to_ucs4 (insert, insertlen,
+                                      (grub_uint8_t *) insertu8,
+                                      insertlen, 0);
+               if (t > 0)
+                 {
+                   if (insert[t-1] == ' ' && buf[lpos] == ' ')
+                     {
+                       insert[t-1] = 0;
+                       if (t != 1)
+                         cl_insert (insert);
+                       lpos++;
+                     }
+                   else
+                     {
+                       insert[t] = 0;
+                       cl_insert (insert);
+                     }
+                 }
+               grub_free (insertu8);
+               grub_free (insert);
+             }
+           cl_set_pos_all ();
+         }
+         break;
 -      case 14:        /* Ctrl-n */
++      case GRUB_TERM_CTRL | 'k':
+         if (lpos < llen)
+           {
+             if (kill_buf)
+               grub_free (kill_buf);
+             kill_buf = grub_malloc ((llen - lpos + 1)
+                                     * sizeof (grub_uint32_t));
+             if (grub_errno)
+               {
+                 grub_print_error ();
+                 grub_errno = GRUB_ERR_NONE;
+               }
+             else
+               {
+                 grub_memcpy (kill_buf, buf + lpos,
+                              (llen - lpos + 1) * sizeof (grub_uint32_t));
+                 kill_buf[llen - lpos] = 0;
+               }
+             cl_delete (llen - lpos);
+           }
+         break;
 -      case 16:        /* Ctrl-p */
++      case GRUB_TERM_CTRL | 'n':
++      case GRUB_TERM_KEY_DOWN:
+         {
+           grub_uint32_t *hist;
+           lpos = 0;
+           if (histpos > 0)
+             {
+               grub_history_replace (histpos, buf, llen);
+               histpos--;
+             }
+           cl_delete (llen);
+           hist = grub_history_get (histpos);
+           cl_insert (hist);
+           break;
+         }
 -      case 21:        /* Ctrl-u */
++
++      case GRUB_TERM_KEY_UP:
++      case GRUB_TERM_CTRL | 'p':
+         {
+           grub_uint32_t *hist;
+           lpos = 0;
+           if (histpos < hist_used - 1)
+             {
+               grub_history_replace (histpos, buf, llen);
+               histpos++;
+             }
+           cl_delete (llen);
+           hist = grub_history_get (histpos);
+           cl_insert (hist);
+         }
+         break;
 -      case 25:        /* Ctrl-y */
++      case GRUB_TERM_CTRL | 'u':
+         if (lpos > 0)
+           {
+             grub_size_t n = lpos;
+             if (kill_buf)
+               grub_free (kill_buf);
+             kill_buf = grub_malloc (n + 1);
+             if (grub_errno)
+               {
+                 grub_print_error ();
+                 grub_errno = GRUB_ERR_NONE;
+               }
+             if (kill_buf)
+               {
+                 grub_memcpy (kill_buf, buf, n);
+                 kill_buf[n] = '\0';
+               }
+             lpos = 0;
+             cl_set_pos_all ();
+             cl_delete (n);
+           }
+         break;
 -      case 4: /* Ctrl-d */
++      case GRUB_TERM_CTRL | 'y':
+         if (kill_buf)
+           cl_insert (kill_buf);
+         break;
+       case '\e':
+         grub_free (cl_terms);
+         return 0;
+       case '\b':
+         if (lpos > 0)
+           {
+             lpos--;
+             cl_set_pos_all ();
+           }
+           else
+             break;
+         /* fall through */
++      case GRUB_TERM_CTRL | 'd':
++      case GRUB_TERM_KEY_DC:
+         if (lpos < llen)
+           cl_delete (1);
+         break;
+       default:
+         if (grub_isprint (key))
+           {
+             grub_uint32_t str[2];
+             str[0] = key;
+             str[1] = '\0';
+             cl_insert (str);
+           }
+         break;
+       }
+       grub_refresh ();
+     }
+   grub_xputs ("\n");
+   grub_refresh ();
+   /* Remove leading spaces.  */
+   lpos = 0;
+   while (buf[lpos] == ' ')
+     lpos++;
+   histpos = 0;
+   if (strlen_ucs4 (buf) > 0)
+     {
+       grub_uint32_t empty[] = { 0 };
+       grub_history_replace (histpos, buf, llen);
+       grub_history_add (empty, 0);
+     }
+   ret = grub_ucs4_to_utf8_alloc (buf + lpos, llen - lpos + 1);
+   grub_free (buf);
+   grub_free (cl_terms);
+   return ret;
+ }
index 0000000000000000000000000000000000000000,cddb152168812e97cd8d79726a99b93a46318ade..849c89e882993948a558882c95995772ba7340ee
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,728 +1,741 @@@
 -    {"delete", GRUB_TERM_DC}
+ /* main.c - the normal mode main routine */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/kernel.h>
+ #include <grub/normal.h>
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+ #include <grub/file.h>
+ #include <grub/mm.h>
+ #include <grub/term.h>
+ #include <grub/env.h>
+ #include <grub/parser.h>
+ #include <grub/reader.h>
+ #include <grub/menu_viewer.h>
+ #include <grub/auth.h>
+ #include <grub/i18n.h>
+ #include <grub/charset.h>
+ #include <grub/script_sh.h>
+ #define GRUB_DEFAULT_HISTORY_SIZE     50
+ static int nested_level = 0;
+ int grub_normal_exit_level = 0;
+ /* Read a line from the file FILE.  */
+ char *
+ grub_file_getline (grub_file_t file)
+ {
+   char c;
+   int pos = 0;
+   int literal = 0;
+   char *cmdline;
+   int max_len = 64;
+   /* Initially locate some space.  */
+   cmdline = grub_malloc (max_len);
+   if (! cmdline)
+     return 0;
+   while (1)
+     {
+       if (grub_file_read (file, &c, 1) != 1)
+       break;
+       /* Skip all carriage returns.  */
+       if (c == '\r')
+       continue;
+       /* Replace tabs with spaces.  */
+       if (c == '\t')
+       c = ' ';
+       /* The previous is a backslash, then...  */
+       if (literal)
+       {
+         /* If it is a newline, replace it with a space and continue.  */
+         if (c == '\n')
+           {
+             c = ' ';
+             /* Go back to overwrite the backslash.  */
+             if (pos > 0)
+               pos--;
+           }
+         literal = 0;
+       }
+       if (c == '\\')
+       literal = 1;
+       if (pos == 0)
+       {
+         if (! grub_isspace (c))
+           cmdline[pos++] = c;
+       }
+       else
+       {
+         if (pos >= max_len)
+           {
+             char *old_cmdline = cmdline;
+             max_len = max_len * 2;
+             cmdline = grub_realloc (cmdline, max_len);
+             if (! cmdline)
+               {
+                 grub_free (old_cmdline);
+                 return 0;
+               }
+           }
+         if (c == '\n')
+           break;
+         cmdline[pos++] = c;
+       }
+     }
+   cmdline[pos] = '\0';
+   /* If the buffer is empty, don't return anything at all.  */
+   if (pos == 0)
+     {
+       grub_free (cmdline);
+       cmdline = 0;
+     }
+   return cmdline;
+ }
+ static void
+ free_menu (grub_menu_t menu)
+ {
+   grub_menu_entry_t entry = menu->entry_list;
+   while (entry)
+     {
+       grub_menu_entry_t next_entry = entry->next;
+       grub_free ((void *) entry->title);
+       grub_free ((void *) entry->sourcecode);
+       entry = next_entry;
+     }
+   grub_free (menu);
+   grub_env_unset_menu ();
+ }
+ static void
+ free_menu_entry_classes (struct grub_menu_entry_class *head)
+ {
+   /* Free all the classes.  */
+   while (head)
+     {
+       struct grub_menu_entry_class *next;
+       grub_free (head->name);
+       next = head->next;
+       grub_free (head);
+       head = next;
+     }
+ }
+ static struct
+ {
+   char *name;
+   int key;
+ } hotkey_aliases[] =
+   {
+     {"backspace", '\b'},
+     {"tab", '\t'},
++    {"delete", GRUB_TERM_KEY_DC},
++    {"insert", GRUB_TERM_KEY_INSERT},
++    {"f1", GRUB_TERM_KEY_F1},
++    {"f2", GRUB_TERM_KEY_F2},
++    {"f3", GRUB_TERM_KEY_F3},
++    {"f4", GRUB_TERM_KEY_F4},
++    {"f5", GRUB_TERM_KEY_F5},
++    {"f6", GRUB_TERM_KEY_F6},
++    {"f7", GRUB_TERM_KEY_F7},
++    {"f8", GRUB_TERM_KEY_F8},
++    {"f9", GRUB_TERM_KEY_F9},
++    {"f10", GRUB_TERM_KEY_F10},
++    {"f11", GRUB_TERM_KEY_F11},
++    {"f12", GRUB_TERM_KEY_F12},
+   };
+ /* Add a menu entry to the current menu context (as given by the environment
+    variable data slot `menu').  As the configuration file is read, the script
+    parser calls this when a menu entry is to be created.  */
+ grub_err_t
+ grub_normal_add_menu_entry (int argc, const char **args,
+                           const char *sourcecode)
+ {
+   const char *menutitle = 0;
+   const char *menusourcecode;
+   grub_menu_t menu;
+   grub_menu_entry_t *last;
+   int failed = 0;
+   int i;
+   struct grub_menu_entry_class *classes_head;  /* Dummy head node for list.  */
+   struct grub_menu_entry_class *classes_tail;
+   char *users = NULL;
+   int hotkey = 0;
+   /* Allocate dummy head node for class list.  */
+   classes_head = grub_zalloc (sizeof (struct grub_menu_entry_class));
+   if (! classes_head)
+     return grub_errno;
+   classes_tail = classes_head;
+   menu = grub_env_get_menu ();
+   if (! menu)
+     return grub_error (GRUB_ERR_MENU, "no menu context");
+   last = &menu->entry_list;
+   menusourcecode = grub_strdup (sourcecode);
+   if (! menusourcecode)
+     return grub_errno;
+   /* Parse menu arguments.  */
+   for (i = 0; i < argc; i++)
+     {
+       /* Capture arguments.  */
+       if (grub_strncmp ("--", args[i], 2) == 0 && i + 1 < argc)
+       {
+         const char *arg = &args[i][2];
+         /* Handle menu class.  */
+         if (grub_strcmp(arg, "class") == 0)
+           {
+             char *class_name;
+             struct grub_menu_entry_class *new_class;
+             i++;
+             class_name = grub_strdup (args[i]);
+             if (! class_name)
+               {
+                 failed = 1;
+                 break;
+               }
+             /* Create a new class and add it at the tail of the list.  */
+             new_class = grub_zalloc (sizeof (struct grub_menu_entry_class));
+             if (! new_class)
+               {
+                 grub_free (class_name);
+                 failed = 1;
+                 break;
+               }
+             /* Fill in the new class node.  */
+             new_class->name = class_name;
+             /* Link the tail to it, and make it the new tail.  */
+             classes_tail->next = new_class;
+             classes_tail = new_class;
+             continue;
+           }
+         else if (grub_strcmp(arg, "users") == 0)
+           {
+             i++;
+             users = grub_strdup (args[i]);
+             if (! users)
+               {
+                 failed = 1;
+                 break;
+               }
+             continue;
+           }
+         else if (grub_strcmp(arg, "hotkey") == 0)
+           {
+             unsigned j;
+             i++;
+             if (args[i][1] == 0)
+               {
+                 hotkey = args[i][0];
+                 continue;
+               }
+             for (j = 0; j < ARRAY_SIZE (hotkey_aliases); j++)
+               if (grub_strcmp (args[i], hotkey_aliases[j].name) == 0)
+                 {
+                   hotkey = hotkey_aliases[j].key;
+                   break;
+                 }
+             if (j < ARRAY_SIZE (hotkey_aliases))
+               continue;
+             failed = 1;
+             grub_error (GRUB_ERR_MENU,
+                         "Invalid hotkey: '%s'.", args[i]);
+             break;
+           }
+         else
+           {
+             /* Handle invalid argument.  */
+             failed = 1;
+             grub_error (GRUB_ERR_MENU,
+                         "invalid argument for menuentry: %s", args[i]);
+             break;
+           }
+       }
+       /* Capture title.  */
+       if (! menutitle)
+       {
+         menutitle = grub_strdup (args[i]);
+       }
+       else
+       {
+         failed = 1;
+         grub_error (GRUB_ERR_MENU,
+                     "too many titles for menuentry: %s", args[i]);
+         break;
+       }
+     }
+   /* Validate arguments.  */
+   if ((! failed) && (! menutitle))
+     {
+       grub_error (GRUB_ERR_MENU, "menuentry is missing title");
+       failed = 1;
+     }
+   /* If argument parsing failed, free any allocated resources.  */
+   if (failed)
+     {
+       free_menu_entry_classes (classes_head);
+       grub_free ((void *) menutitle);
+       grub_free ((void *) menusourcecode);
+       /* Here we assume that grub_error has been used to specify failure details.  */
+       return grub_errno;
+     }
+   /* Add the menu entry at the end of the list.  */
+   while (*last)
+     last = &(*last)->next;
+   *last = grub_zalloc (sizeof (**last));
+   if (! *last)
+     {
+       free_menu_entry_classes (classes_head);
+       grub_free ((void *) menutitle);
+       grub_free ((void *) menusourcecode);
+       return grub_errno;
+     }
+   (*last)->title = menutitle;
+   (*last)->hotkey = hotkey;
+   (*last)->classes = classes_head;
+   if (users)
+     (*last)->restricted = 1;
+   (*last)->users = users;
+   (*last)->sourcecode = menusourcecode;
+   menu->size++;
+   return GRUB_ERR_NONE;
+ }
+ static grub_menu_t
+ read_config_file (const char *config)
+ {
+   grub_file_t file;
+   auto grub_err_t getline (char **line, int cont);
+   grub_err_t getline (char **line, int cont __attribute__ ((unused)))
+     {
+       while (1)
+       {
+         char *buf;
+         *line = buf = grub_file_getline (file);
+         if (! buf)
+           return grub_errno;
+         if (buf[0] == '#')
+           grub_free (*line);
+         else
+           break;
+       }
+       return GRUB_ERR_NONE;
+     }
+   grub_menu_t newmenu;
+   newmenu = grub_env_get_menu ();
+   if (! newmenu)
+     {
+       newmenu = grub_zalloc (sizeof (*newmenu));
+       if (! newmenu)
+       return 0;
+       grub_env_set_menu (newmenu);
+     }
+   /* Try to open the config file.  */
+   file = grub_file_open (config);
+   if (! file)
+     return 0;
+   while (1)
+     {
+       char *line;
+       /* Print an error, if any.  */
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       if ((getline (&line, 0)) || (! line))
+       break;
+       grub_normal_parse_line (line, getline);
+       grub_free (line);
+     }
+   grub_file_close (file);
+   return newmenu;
+ }
+ /* Initialize the screen.  */
+ void
+ grub_normal_init_page (struct grub_term_output *term)
+ {
+   int msg_len;
+   int posx;
+   const char *msg = _("GNU GRUB  version %s");
+   char *msg_formatted;
+   grub_uint32_t *unicode_msg;
+   grub_uint32_t *last_position;
+  
+   grub_term_cls (term);
+   msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION);
+   if (!msg_formatted)
+     return;
+  
+   msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
+                                    &unicode_msg, &last_position);
+   grub_free (msg_formatted);
+  
+   if (msg_len < 0)
+     {
+       return;
+     }
+   posx = grub_getstringwidth (unicode_msg, last_position, term);
+   posx = (grub_term_width (term) - posx) / 2;
+   grub_term_gotoxy (term, posx, 1);
+   grub_print_ucs4 (unicode_msg, last_position, 0, 0, term);
+   grub_putcode ('\n', term);
+   grub_putcode ('\n', term);
+   grub_free (unicode_msg);
+ }
+ static void
+ read_lists (const char *val)
+ {
+   if (! grub_no_autoload)
+     {
+       read_command_list (val);
+       read_fs_list (val);
+       read_crypto_list (val);
+       read_terminal_list (val);
+     }
+ }
+ static char *
+ read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
+                const char *val)
+ {
+   read_lists (val);
+   return val ? grub_strdup (val) : NULL;
+ }
+ /* Read the config file CONFIG and execute the menu interface or
+    the command line interface if BATCH is false.  */
+ void
+ grub_normal_execute (const char *config, int nested, int batch)
+ {
+   grub_menu_t menu = 0;
+   const char *prefix;
+   if (! nested)
+     {
+       prefix = grub_env_get ("prefix");
+       read_lists (prefix);
+       grub_register_variable_hook ("prefix", NULL, read_lists_hook);
+       grub_command_execute ("parser.grub", 0, 0);
+     }
+   if (config)
+     {
+       menu = read_config_file (config);
+       /* Ignore any error.  */
+       grub_errno = GRUB_ERR_NONE;
+     }
+   if (! batch)
+     {
+       if (menu && menu->size)
+       {
+         grub_show_menu (menu, nested);
+         if (nested)
+           free_menu (menu);
+       }
+     }
+ }
+ /* This starts the normal mode.  */
+ void
+ grub_enter_normal_mode (const char *config)
+ {
+   nested_level++;
+   grub_normal_execute (config, 0, 0);
+   grub_cmdline_run (0);
+   nested_level--;
+   if (grub_normal_exit_level)
+     grub_normal_exit_level--;
+ }
+ /* Enter normal mode from rescue mode.  */
+ static grub_err_t
+ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
+                int argc, char *argv[])
+ {
+   if (argc == 0)
+     {
+       /* Guess the config filename. It is necessary to make CONFIG static,
+        so that it won't get broken by longjmp.  */
+       char *config;
+       const char *prefix;
+       prefix = grub_env_get ("prefix");
+       if (prefix)
+       {
+         config = grub_xasprintf ("%s/grub.cfg", prefix);
+         if (! config)
+           goto quit;
+         grub_enter_normal_mode (config);
+         grub_free (config);
+       }
+       else
+       grub_enter_normal_mode (0);
+     }
+   else
+     grub_enter_normal_mode (argv[0]);
+ quit:
+   return 0;
+ }
+ /* Exit from normal mode to rescue mode.  */
+ static grub_err_t
+ grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
+                     int argc __attribute__ ((unused)),
+                     char *argv[] __attribute__ ((unused)))
+ {
+   if (nested_level <= grub_normal_exit_level)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
+   grub_normal_exit_level++;
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_normal_reader_init (int nested)
+ {
+   struct grub_term_output *term;
+   const char *msg = _("Minimal BASH-like line editing is supported. For "
+                     "the first word, TAB lists possible command completions. Anywhere "
+                     "else TAB lists possible device or file completions. %s");
+   const char *msg_esc = _("ESC at any time exits.");
+   char *msg_formatted;
+   msg_formatted = grub_xasprintf (msg, nested ? msg_esc : "");
+   if (!msg_formatted)
+     return grub_errno;
+   FOR_ACTIVE_TERM_OUTPUTS(term)
+   {
+     grub_normal_init_page (term);
+     grub_term_setcursor (term, 1);
+     grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
+     grub_putcode ('\n', term);
+     grub_putcode ('\n', term);
+   }
+   grub_free (msg_formatted);
+  
+   return 0;
+ }
+ static grub_err_t
+ grub_normal_read_line_real (char **line, int cont, int nested)
+ {
+   const char *prompt;
+   if (cont)
+     prompt = ">";
+   else
+     prompt = "grub>";
+   if (!prompt)
+     return grub_errno;
+   while (1)
+     {
+       *line = grub_cmdline_get (prompt);
+       if (*line)
+       break;
+       if (cont || nested)
+       {
+         grub_free (*line);
+         *line = 0;
+         return grub_errno;
+       }
+     }
+   
+   return 0;
+ }
+ static grub_err_t
+ grub_normal_read_line (char **line, int cont)
+ {
+   return grub_normal_read_line_real (line, cont, 0);
+ }
+ void
+ grub_cmdline_run (int nested)
+ {
+   grub_err_t err = GRUB_ERR_NONE;
+   err = grub_auth_check_authentication (NULL);
+   if (err)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return;
+     }
+   grub_normal_reader_init (nested);
+   while (1)
+     {
+       char *line;
+       if (grub_normal_exit_level)
+       break;
+       /* Print an error, if any.  */
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       grub_normal_read_line_real (&line, 0, nested);
+       if (! line)
+       break;
+       grub_normal_parse_line (line, grub_normal_read_line);
+       grub_free (line);
+     }
+ }
+ static char *
+ grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
+                     const char *val)
+ {
+   grub_set_more ((*val == '1'));
+   return grub_strdup (val);
+ }
+ /* clear */
+ static grub_err_t
+ grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
+                  int argc __attribute__ ((unused)),
+                  char *argv[] __attribute__ ((unused)))
+ {
+   grub_cls ();
+   return 0;
+ }
+ static grub_command_t cmd_clear;
+ static void (*grub_xputs_saved) (const char *str);
+ GRUB_MOD_INIT(normal)
+ {
+   grub_context_init ();
+   grub_script_init ();
+   grub_xputs_saved = grub_xputs;
+   grub_xputs = grub_xputs_normal;
+   /* Normal mode shouldn't be unloaded.  */
+   if (mod)
+     grub_dl_ref (mod);
+   cmd_clear =
+     grub_register_command ("clear", grub_mini_cmd_clear,
+                          0, N_("Clear the screen."));
+   grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
+   grub_register_variable_hook ("pager", 0, grub_env_write_pager);
+   /* Register a command "normal" for the rescue mode.  */
+   grub_register_command ("normal", grub_cmd_normal,
+                        0, N_("Enter normal mode."));
+   grub_register_command ("normal_exit", grub_cmd_normal_exit,
+                        0, N_("Exit from normal mode."));
+   /* Reload terminal colors when these variables are written to.  */
+   grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
+   grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
+   /* Preserve hooks after context changes.  */
+   grub_env_export ("color_normal");
+   grub_env_export ("color_highlight");
+   /* Set default color names.  */
+   grub_env_set ("color_normal", "white/black");
+   grub_env_set ("color_highlight", "black/white");
+ }
+ GRUB_MOD_FINI(normal)
+ {
+   grub_context_fini ();
+   grub_script_fini ();
+   grub_xputs = grub_xputs_saved;
+   grub_set_history (0);
+   grub_register_variable_hook ("pager", 0, 0);
+   grub_fs_autoload_hook = 0;
+   grub_unregister_command (cmd_clear);
+ }
index 0000000000000000000000000000000000000000,cc84ce38c186b681c34b70cb53b4ebc9e83f36a9..12a426b39c3a93f76f7581781efc7c7bc4f6276d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,662 +1,669 @@@
 -        c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
+ /* menu.c - General supporting functionality for menus.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/normal.h>
+ #include <grub/misc.h>
+ #include <grub/loader.h>
+ #include <grub/mm.h>
+ #include <grub/time.h>
+ #include <grub/env.h>
+ #include <grub/menu_viewer.h>
+ #include <grub/command.h>
+ #include <grub/parser.h>
+ #include <grub/auth.h>
+ #include <grub/i18n.h>
+ #include <grub/term.h>
+ #include <grub/script_sh.h>
+ /* Time to delay after displaying an error message about a default/fallback
+    entry failing to boot.  */
+ #define DEFAULT_ENTRY_ERROR_DELAY_MS  2500
+ grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
+                                    int nested) = NULL;
+ /* Wait until the user pushes any key so that the user
+    can see what happened.  */
+ void
+ grub_wait_after_message (void)
+ {
+   grub_uint64_t endtime;
+   grub_xputs ("\n");
+   grub_printf_ (N_("Press any key to continue..."));
+   grub_refresh ();
+   endtime = grub_get_time_ms () + 10000;
+   while (grub_get_time_ms () < endtime)
+     if (grub_checkkey () >= 0)
+       {
+       grub_getkey ();
+       break;
+       }
+   grub_xputs ("\n");
+ }
+ /* Get a menu entry by its index in the entry list.  */
+ grub_menu_entry_t
+ grub_menu_get_entry (grub_menu_t menu, int no)
+ {
+   grub_menu_entry_t e;
+   for (e = menu->entry_list; e && no > 0; e = e->next, no--)
+     ;
+   return e;
+ }
+ /* Return the current timeout. If the variable "timeout" is not set or
+    invalid, return -1.  */
+ int
+ grub_menu_get_timeout (void)
+ {
+   char *val;
+   int timeout;
+   val = grub_env_get ("timeout");
+   if (! val)
+     return -1;
+   grub_error_push ();
+   timeout = (int) grub_strtoul (val, 0, 0);
+   /* If the value is invalid, unset the variable.  */
+   if (grub_errno != GRUB_ERR_NONE)
+     {
+       grub_env_unset ("timeout");
+       grub_errno = GRUB_ERR_NONE;
+       timeout = -1;
+     }
+   grub_error_pop ();
+   return timeout;
+ }
+ /* Set current timeout in the variable "timeout".  */
+ void
+ grub_menu_set_timeout (int timeout)
+ {
+   /* Ignore TIMEOUT if it is zero, because it will be unset really soon.  */
+   if (timeout > 0)
+     {
+       char buf[16];
+       grub_snprintf (buf, sizeof (buf), "%d", timeout);
+       grub_env_set ("timeout", buf);
+     }
+ }
+ /* Get the first entry number from the value of the environment variable NAME,
+    which is a space-separated list of non-negative integers.  The entry number
+    which is returned is stripped from the value of NAME.  If no entry number
+    can be found, -1 is returned.  */
+ static int
+ get_and_remove_first_entry_number (const char *name)
+ {
+   char *val;
+   char *tail;
+   int entry;
+   val = grub_env_get (name);
+   if (! val)
+     return -1;
+   grub_error_push ();
+   entry = (int) grub_strtoul (val, &tail, 0);
+   if (grub_errno == GRUB_ERR_NONE)
+     {
+       /* Skip whitespace to find the next digit.  */
+       while (*tail && grub_isspace (*tail))
+       tail++;
+       grub_env_set (name, tail);
+     }
+   else
+     {
+       grub_env_unset (name);
+       grub_errno = GRUB_ERR_NONE;
+       entry = -1;
+     }
+   grub_error_pop ();
+   return entry;
+ }
+ static void
+ grub_menu_execute_entry_real (grub_menu_entry_t entry)
+ {
+   const char *source;
+   auto grub_err_t getline (char **line, int cont);
+   grub_err_t getline (char **line, int cont __attribute__ ((unused)))
+   {
+     const char *p;
+     if (!source)
+       {
+       *line = 0;
+       return 0;
+       }
+     p = grub_strchr (source, '\n');
+     if (p)
+       *line = grub_strndup (source, p - source);
+     else
+       *line = grub_strdup (source);
+     source = p ? p + 1 : 0;
+     return 0;
+   }
+   source = entry->sourcecode;
+   while (source)
+     {
+       char *line;
+       getline (&line, 0);
+       grub_normal_parse_line (line, getline);
+       grub_free (line);
+     }
+ }
+ /* Run a menu entry.  */
+ void
+ grub_menu_execute_entry(grub_menu_entry_t entry)
+ {
+   grub_err_t err = GRUB_ERR_NONE;
+   if (entry->restricted)
+     err = grub_auth_check_authentication (entry->users);
+   if (err)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return;
+     }
+   grub_env_set ("chosen", entry->title);
+   grub_menu_execute_entry_real (entry);
+   if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+     /* Implicit execution of boot, only if something is loaded.  */
+     grub_command_execute ("boot", 0, 0);
+ }
+ /* Execute ENTRY from the menu MENU, falling back to entries specified
+    in the environment variable "fallback" if it fails.  CALLBACK is a
+    pointer to a struct of function pointers which are used to allow the
+    caller provide feedback to the user.  */
+ void
+ grub_menu_execute_with_fallback (grub_menu_t menu,
+                                grub_menu_entry_t entry,
+                                grub_menu_execute_callback_t callback,
+                                void *callback_data)
+ {
+   int fallback_entry;
+   callback->notify_booting (entry, callback_data);
+   grub_menu_execute_entry (entry);
+   /* Deal with fallback entries.  */
+   while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
+        >= 0)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       entry = grub_menu_get_entry (menu, fallback_entry);
+       callback->notify_fallback (entry, callback_data);
+       grub_menu_execute_entry (entry);
+       /* If the function call to execute the entry returns at all, then this is
+        taken to indicate a boot failure.  For menu entries that do something
+        other than actually boot an operating system, this could assume
+        incorrectly that something failed.  */
+     }
+   callback->notify_failure (callback_data);
+ }
+ static struct grub_menu_viewer *viewers;
+ static void
+ menu_set_chosen_entry (int entry)
+ {
+   struct grub_menu_viewer *cur;
+   for (cur = viewers; cur; cur = cur->next)
+     cur->set_chosen_entry (entry, cur->data);
+ }
+ static void
+ menu_print_timeout (int timeout)
+ {
+   struct grub_menu_viewer *cur;
+   for (cur = viewers; cur; cur = cur->next)
+     cur->print_timeout (timeout, cur->data);
+ }
+ static void
+ menu_fini (void)
+ {
+   struct grub_menu_viewer *cur, *next;
+   for (cur = viewers; cur; cur = next)
+     {
+       next = cur->next;
+       cur->fini (cur->data);
+       grub_free (cur);
+     }
+   viewers = NULL;
+ }
+ static void
+ menu_init (int entry, grub_menu_t menu, int nested)
+ {
+   struct grub_term_output *term;
+   FOR_ACTIVE_TERM_OUTPUTS(term)
+   {
+     grub_err_t err;
+     if (grub_gfxmenu_try_hook && grub_strcmp (term->name, "gfxterm") == 0)
+       {
+       err = grub_gfxmenu_try_hook (entry, menu, nested);
+       if(!err)
+         continue;
+       grub_errno = GRUB_ERR_NONE;
+       }
+     err = grub_menu_try_text (term, entry, menu, nested);
+     if(!err)
+       continue;
+     grub_print_error ();
+     grub_errno = GRUB_ERR_NONE;
+   }
+ }
+ static void
+ clear_timeout (void)
+ {
+   struct grub_menu_viewer *cur;
+   for (cur = viewers; cur; cur = cur->next)
+     cur->clear_timeout (cur->data);
+ }
+ void
+ grub_menu_register_viewer (struct grub_menu_viewer *viewer)
+ {
+   viewer->next = viewers;
+   viewers = viewer;
+ }
+ /* Get the entry number from the variable NAME.  */
+ static int
+ get_entry_number (grub_menu_t menu, const char *name)
+ {
+   char *val;
+   int entry;
+   val = grub_env_get (name);
+   if (! val)
+     return -1;
+   grub_error_push ();
+   entry = (int) grub_strtoul (val, 0, 0);
+   if (grub_errno == GRUB_ERR_BAD_NUMBER)
+     {
+       /* See if the variable matches the title of a menu entry.  */
+       grub_menu_entry_t e = menu->entry_list;
+       int i;
+       grub_errno = GRUB_ERR_NONE;
+       for (i = 0; e; i++)
+       {
+         if (grub_strcmp (e->title, val) == 0)
+           {
+             entry = i;
+             break;
+           }
+         e = e->next;
+       }
+       if (! e)
+       entry = -1;
+     }
+   if (grub_errno != GRUB_ERR_NONE)
+     {
+       grub_errno = GRUB_ERR_NONE;
+       entry = -1;
+     }
+   grub_error_pop ();
+   return entry;
+ }
+ #define GRUB_MENU_PAGE_SIZE 10
+ /* Show the menu and handle menu entry selection.  Returns the menu entry
+    index that should be executed or -1 if no entry should be executed (e.g.,
+    Esc pressed to exit a sub-menu or switching menu viewers).
+    If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
+    entry to be executed is a result of an automatic default selection because
+    of the timeout.  */
+ static int
+ run_menu (grub_menu_t menu, int nested, int *auto_boot)
+ {
+   grub_uint64_t saved_time;
+   int default_entry, current_entry;
+   int timeout;
+   default_entry = get_entry_number (menu, "default");
+   /* If DEFAULT_ENTRY is not within the menu entries, fall back to
+      the first entry.  */
+   if (default_entry < 0 || default_entry >= menu->size)
+     default_entry = 0;
+   /* If timeout is 0, drawing is pointless (and ugly).  */
+   if (grub_menu_get_timeout () == 0)
+     {
+       *auto_boot = 1;
+       return default_entry;
+     }
+   current_entry = default_entry;
+   /* Initialize the time.  */
+   saved_time = grub_get_time_ms ();
+  refresh:
+   menu_init (current_entry, menu, nested);
+   timeout = grub_menu_get_timeout ();
+   if (timeout > 0)
+     menu_print_timeout (timeout);
+   else
+     clear_timeout ();
+   while (1)
+     {
+       int c;
+       timeout = grub_menu_get_timeout ();
+       if (grub_normal_exit_level)
+       return -1;
+       if (timeout > 0)
+       {
+         grub_uint64_t current_time;
+         current_time = grub_get_time_ms ();
+         if (current_time - saved_time >= 1000)
+           {
+             timeout--;
+             grub_menu_set_timeout (timeout);
+             saved_time = current_time;
+             menu_print_timeout (timeout);
+           }
+       }
+       if (timeout == 0)
+       {
+         grub_env_unset ("timeout");
+           *auto_boot = 1;
+         menu_fini ();
+         return default_entry;
+       }
+       if (grub_checkkey () >= 0 || timeout < 0)
+       {
 -          case GRUB_TERM_HOME:
++        c = grub_getkey ();
+         if (timeout >= 0)
+           {
+             grub_env_unset ("timeout");
+             grub_env_unset ("fallback");
+             clear_timeout ();
+           }
+         switch (c)
+           {
 -          case GRUB_TERM_END:
++          case GRUB_TERM_KEY_HOME:
++          case GRUB_TERM_CTRL | 'a':
+             current_entry = 0;
+             menu_set_chosen_entry (current_entry);
+             break;
 -          case GRUB_TERM_UP:
++          case GRUB_TERM_KEY_END:
++          case GRUB_TERM_CTRL | 'e':
+             current_entry = menu->size - 1;
+             menu_set_chosen_entry (current_entry);
+             break;
 -          case GRUB_TERM_DOWN:
++          case GRUB_TERM_KEY_UP:
++          case GRUB_TERM_CTRL | 'p':
+           case '^':
+             if (current_entry > 0)
+               current_entry--;
+             menu_set_chosen_entry (current_entry);
+             break;
 -          case GRUB_TERM_PPAGE:
++          case GRUB_TERM_CTRL | 'n':
++          case GRUB_TERM_KEY_DOWN:
+           case 'v':
+             if (current_entry < menu->size - 1)
+               current_entry++;
+             menu_set_chosen_entry (current_entry);
+             break;
 -          case GRUB_TERM_NPAGE:
++          case GRUB_TERM_CTRL | 'g':
++          case GRUB_TERM_KEY_PPAGE:
+             if (current_entry < GRUB_MENU_PAGE_SIZE)
+               current_entry = 0;
+             else
+               current_entry -= GRUB_MENU_PAGE_SIZE;
+             menu_set_chosen_entry (current_entry);
+             break;
 -          case 6:
++          case GRUB_TERM_CTRL | 'c':
++          case GRUB_TERM_KEY_NPAGE:
+             if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
+               current_entry += GRUB_MENU_PAGE_SIZE;
+             else
+               current_entry = menu->size - 1;
+             menu_set_chosen_entry (current_entry);
+             break;
+           case '\n':
+           case '\r':
++          case GRUB_TERM_KEY_RIGHT:
++          case GRUB_TERM_CTRL | 'f':
+             menu_fini ();
+               *auto_boot = 0;
+             return current_entry;
+           case '\e':
+             if (nested)
+               {
+                 menu_fini ();
+                 return -1;
+               }
+             break;
+           case 'c':
+             menu_fini ();
+             grub_cmdline_run (1);
+             goto refresh;
+           case 'e':
+             menu_fini ();
+               {
+                 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
+                 if (e)
+                   grub_menu_entry_run (e);
+               }
+             goto refresh;
+           default:
+             {
+               grub_menu_entry_t entry;
+               int i;
+               for (i = 0, entry = menu->entry_list; i < menu->size;
+                    i++, entry = entry->next)
+                 if (entry->hotkey == c)
+                   {
+                     menu_fini ();
+                     *auto_boot = 0;
+                     return i;
+                   }
+             }
+             break;
+           }
+       }
+     }
+   /* Never reach here.  */
+   return -1;
+ }
+ /* Callback invoked immediately before a menu entry is executed.  */
+ static void
+ notify_booting (grub_menu_entry_t entry,
+               void *userdata __attribute__((unused)))
+ {
+   grub_printf ("  ");
+   grub_printf_ (N_("Booting \'%s\'"), entry->title);
+   grub_printf ("\n\n");
+ }
+ /* Callback invoked when a default menu entry executed because of a timeout
+    has failed and an attempt will be made to execute the next fallback
+    entry, ENTRY.  */
+ static void
+ notify_fallback (grub_menu_entry_t entry,
+                void *userdata __attribute__((unused)))
+ {
+   grub_printf ("\n   ");
+   grub_printf_ (N_("Falling back to \'%s\'"), entry->title);
+   grub_printf ("\n\n");
+   grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
+ }
+ /* Callback invoked when a menu entry has failed and there is no remaining
+    fallback entry to attempt.  */
+ static void
+ notify_execution_failure (void *userdata __attribute__((unused)))
+ {
+   if (grub_errno != GRUB_ERR_NONE)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+     }
+   grub_printf ("\n  ");
+   grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
+   grub_wait_after_message ();
+ }
+ /* Callbacks used by the text menu to provide user feedback when menu entries
+    are executed.  */
+ static struct grub_menu_execute_callback execution_callback =
+ {
+   .notify_booting = notify_booting,
+   .notify_fallback = notify_fallback,
+   .notify_failure = notify_execution_failure
+ };
+ static grub_err_t
+ show_menu (grub_menu_t menu, int nested)
+ {
+   while (1)
+     {
+       int boot_entry;
+       grub_menu_entry_t e;
+       int auto_boot;
+       boot_entry = run_menu (menu, nested, &auto_boot);
+       if (boot_entry < 0)
+       break;
+       e = grub_menu_get_entry (menu, boot_entry);
+       if (! e)
+       continue; /* Menu is empty.  */
+       grub_cls ();
+       if (auto_boot)
+         {
+           grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
+         }
+       else
+         {
+         int chars_before = grub_normal_get_char_counter ();
+           grub_errno = GRUB_ERR_NONE;
+           grub_menu_execute_entry (e);
+         grub_print_error ();
+         grub_errno = GRUB_ERR_NONE;
+           if (chars_before != grub_normal_get_char_counter ())
+           grub_wait_after_message ();
+         }
+     }
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_show_menu (grub_menu_t menu, int nested)
+ {
+   grub_err_t err1, err2;
+   while (1)
+     {
+       err1 = show_menu (menu, nested);
+       grub_print_error ();
+       if (grub_normal_exit_level)
+       break;
+       err2 = grub_auth_check_authentication (NULL);
+       if (err2)
+       {
+         grub_print_error ();
+         grub_errno = GRUB_ERR_NONE;
+         continue;
+       }
+       break;
+     }
+   return err1;
+ }
index 0000000000000000000000000000000000000000,238c94ecde40084fa90e15de5c89ce30d416fdab..87292d445eea078d042f133298b3cd76edd9fc67
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1414 +1,1425 @@@
 -      int c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2005,2006,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/normal.h>
+ #include <grub/term.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/loader.h>
+ #include <grub/command.h>
+ #include <grub/parser.h>
+ #include <grub/script_sh.h>
+ #include <grub/auth.h>
+ #include <grub/i18n.h>
+ #include <grub/charset.h>
+ enum update_mode
+   {
+     NO_LINE,
+     SINGLE_LINE,
+     ALL_LINES
+   };
+ struct line
+ {
+   /* The line buffer.  */
+   char *buf;
+   /* The length of the line.  */
+   int len;
+   /* The maximum length of the line.  */
+   int max_len;
+ };
+ struct per_term_screen
+ {
+   struct grub_term_output *term;
+   /* The X coordinate.  */
+   int x;
+   /* The Y coordinate.  */
+   int y;
+ };
+ struct screen
+ {
+   /* The array of lines.  */
+   struct line *lines;
+   /* The number of lines.  */
+   int num_lines;
+   /* The current column.  */
+   int column;
+   /* The real column.  */
+   int real_column;
+   /* The current line.  */
+   int line;
+   /* The kill buffer.  */
+   char *killed_text;
+   /* The flag of a completion window.  */
+   int completion_shown;
+   struct per_term_screen *terms;
+   unsigned nterms;
+ };
+ /* Used for storing completion items temporarily.  */
+ static struct line completion_buffer;
+ static int completion_type;
+ /* Initialize a line.  */
+ static int
+ init_line (struct line *linep)
+ {
+   linep->len = 0;
+   linep->max_len = 80; /* XXX */
+   linep->buf = grub_malloc (linep->max_len);
+   if (! linep->buf)
+     return 0;
+   return 1;
+ }
+ /* Allocate extra space if necessary.  */
+ static int
+ ensure_space (struct line *linep, int extra)
+ {
+   if (linep->max_len < linep->len + extra)
+     {
+       linep->max_len = linep->len + extra + 80; /* XXX */
+       linep->buf = grub_realloc (linep->buf, linep->max_len + 1);
+       if (! linep->buf)
+       return 0;
+     }
+   return 1;
+ }
+ /* Return the number of lines occupied by this line on the screen.  */
+ static int
+ get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
+ {
+   return (linep->len / grub_term_entry_width (term_screen->term)) + 1;
+ }
+ /* Print a line.  */
+ static void
+ print_line (struct line *linep, int offset, int start, int y,
+           struct per_term_screen *term_screen)
+ {
+   grub_term_gotoxy (term_screen->term, 
+                   GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + start + 1,
+                   y + GRUB_TERM_FIRST_ENTRY_Y);
+   if (linep->len >= offset + grub_term_entry_width (term_screen->term))
+     {
+       char *p, c;
+       p = linep->buf + offset + grub_term_entry_width (term_screen->term);
+       c = *p;
+       *p = 0;
+       grub_puts_terminal (linep->buf + offset + start, term_screen->term);
+       *p = c;
+       grub_putcode ('\\', term_screen->term);
+     }
+   else
+     {
+       int i;
+       char *p, c;
+       p = linep->buf + linep->len;
+       c = *p;
+       *p = 0;
+       grub_puts_terminal (linep->buf + offset + start, term_screen->term);
+       *p = c;
+       for (i = 0;
+          i <= grub_term_entry_width (term_screen->term) - linep->len + offset;
+          i++)
+       grub_putcode (' ', term_screen->term);
+     }
+ }
+ /* Print an empty line.  */
+ static void
+ print_empty_line (int y, struct per_term_screen *term_screen)
+ {
+   int i;
+   grub_term_gotoxy (term_screen->term,
+                   GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1,
+                   y + GRUB_TERM_FIRST_ENTRY_Y);
+   for (i = 0; i < grub_term_entry_width (term_screen->term) + 1; i++)
+     grub_putcode (' ', term_screen->term);
+ }
+ /* Print an up arrow.  */
+ static void
+ print_up (int flag, struct per_term_screen *term_screen)
+ {
+   grub_term_gotoxy (term_screen->term, GRUB_TERM_LEFT_BORDER_X 
+                   + grub_term_entry_width (term_screen->term),
+                   GRUB_TERM_FIRST_ENTRY_Y);
+   if (flag)
+     grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
+   else
+     grub_putcode (' ', term_screen->term);
+ }
+ /* Print a down arrow.  */
+ static void
+ print_down (int flag, struct per_term_screen *term_screen)
+ {
+   grub_term_gotoxy (term_screen->term, GRUB_TERM_LEFT_BORDER_X
+                   + grub_term_border_width (term_screen->term),
+                   GRUB_TERM_TOP_BORDER_Y 
+                   + grub_term_num_entries (term_screen->term));
+   if (flag)
+     grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
+   else
+     grub_putcode (' ', term_screen->term);
+ }
+ /* Draw the lines of the screen SCREEN.  */
+ static void
+ update_screen (struct screen *screen, struct per_term_screen *term_screen,
+              int region_start, int region_column,
+              int up, int down, enum update_mode mode)
+ {
+   int up_flag = 0;
+   int down_flag = 0;
+   int y;
+   int i;
+   struct line *linep;
+   /* Check if scrolling is necessary.  */
+   if (term_screen->y < 0 || term_screen->y
+       >= grub_term_num_entries (term_screen->term))
+     {
+       if (term_screen->y < 0)
+       term_screen->y = 0;
+       else
+       term_screen->y = grub_term_num_entries (term_screen->term) - 1;
+       region_start = 0;
+       region_column = 0;
+       up = 1;
+       down = 1;
+       mode = ALL_LINES;
+     }
+   if (mode != NO_LINE)
+     {
+       /* Draw lines. This code is tricky, because this must calculate logical
+        positions.  */
+       y = term_screen->y - screen->column
+       / grub_term_entry_width (term_screen->term);
+       i = screen->line;
+       linep = screen->lines + i;
+       while (y > 0)
+       {
+          i--;
+          linep--;
+          y -= get_logical_num_lines (linep, term_screen);
+       }
+       if (y < 0 || i > 0)
+       up_flag = 1;
+       do
+       {
+         int column;
+         if (linep >= screen->lines + screen->num_lines)
+           break;
+         for (column = 0;
+              column <= linep->len
+                && y < grub_term_num_entries (term_screen->term);
+              column += grub_term_entry_width (term_screen->term), y++)
+           {
+             if (y < 0)
+               continue;
+             if (i == region_start)
+               {
+                 if (region_column >= column
+                     && region_column
+                     < (column
+                        + grub_term_entry_width (term_screen->term)))
+                   print_line (linep, column, region_column - column, y,
+                               term_screen);
+                 else if (region_column < column)
+                   print_line (linep, column, 0, y, term_screen);
+               }
+             else if (i > region_start && mode == ALL_LINES)
+               print_line (linep, column, 0, y, term_screen);
+           }
+         if (y == grub_term_num_entries (term_screen->term))
+           {
+             if (column <= linep->len || i + 1 < screen->num_lines)
+               down_flag = 1;
+           }
+         linep++;
+         i++;
+         if (mode == ALL_LINES && i == screen->num_lines)
+           for (; y < grub_term_num_entries (term_screen->term); y++)
+             print_empty_line (y, term_screen);
+       }
+       while (y < grub_term_num_entries (term_screen->term));
+       /* Draw up and down arrows.  */
+       if (up)
+       print_up (up_flag, term_screen);
+       if (down)
+       print_down (down_flag, term_screen);
+     }
+   /* Place the cursor.  */
+   grub_term_gotoxy (term_screen->term, 
+                   GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1
+                   + term_screen->x,
+                   GRUB_TERM_FIRST_ENTRY_Y + term_screen->y);
+   grub_term_refresh (term_screen->term);
+ }
+ static void
+ update_screen_all (struct screen *screen,
+                  int region_start, int region_column,
+                  int up, int down, enum update_mode mode)
+ {
+   unsigned i;
+   for (i = 0; i < screen->nterms; i++)
+     update_screen (screen, &screen->terms[i], region_start, region_column,
+                  up, down, mode);
+ }
+ static int
+ insert_string (struct screen *screen, char *s, int update)
+ {
+   int region_start = screen->num_lines;
+   int region_column = 0;
+   int down[screen->nterms];
+   enum update_mode mode[screen->nterms];
+   unsigned i;
+   for (i = 0; i < screen->nterms; i++)
+     {
+       down[i] = 0;
+       mode[i] = NO_LINE;
+     }
+   while (*s)
+     {
+       if (*s == '\n')
+       {
+         /* LF is special because it creates a new line.  */
+         struct line *current_linep;
+         struct line *next_linep;
+         int size;
+         /* Make a new line.  */
+         screen->num_lines++;
+         screen->lines = grub_realloc (screen->lines,
+                                       screen->num_lines
+                                       * sizeof (screen->lines[0]));
+         if (! screen->lines)
+           return 0;
+         /* Scroll down. */
+         grub_memmove (screen->lines + screen->line + 2,
+                       screen->lines + screen->line + 1,
+                       ((screen->num_lines - screen->line - 2)
+                        * sizeof (struct line)));
+         if (! init_line (screen->lines + screen->line + 1))
+           return 0;
+         /* Fold the line.  */
+         current_linep = screen->lines + screen->line;
+         next_linep = current_linep + 1;
+         size = current_linep->len - screen->column;
+         if (! ensure_space (next_linep, size))
+           return 0;
+         grub_memmove (next_linep->buf,
+                       current_linep->buf + screen->column,
+                       size);
+         current_linep->len = screen->column;
+         next_linep->len = size;
+         /* Update a dirty region.  */
+         if (region_start > screen->line)
+           {
+             region_start = screen->line;
+             region_column = screen->column;
+           }
+         for (i = 0; i < screen->nterms; i++)
+           {
+             mode[i] = ALL_LINES;
+             down[i] = 1; /* XXX not optimal.  */
+           }
+         /* Move the cursor.  */
+         screen->column = screen->real_column = 0;
+         screen->line++;
+         for (i = 0; i < screen->nterms; i++)
+           {
+             screen->terms[i].x = 0;
+             screen->terms[i].y++;
+           }
+         s++;
+       }
+       else
+       {
+         /* All but LF.  */
+         char *p;
+         struct line *current_linep;
+         int size;
+         int orig_num[screen->nterms], new_num[screen->nterms];
+         /* Find a string delimited by LF.  */
+         p = grub_strchr (s, '\n');
+         if (! p)
+           p = s + grub_strlen (s);
+         /* Insert the string.  */
+         current_linep = screen->lines + screen->line;
+         size = p - s;
+         if (! ensure_space (current_linep, size))
+           return 0;
+         grub_memmove (current_linep->buf + screen->column + size,
+                       current_linep->buf + screen->column,
+                       current_linep->len - screen->column);
+         grub_memmove (current_linep->buf + screen->column,
+                       s,
+                       size);
+         for (i = 0; i < screen->nterms; i++)
+           orig_num[i] = get_logical_num_lines (current_linep,
+                                                &screen->terms[i]);
+         current_linep->len += size;
+         for (i = 0; i < screen->nterms; i++)
+           new_num[i] = get_logical_num_lines (current_linep,
+                                               &screen->terms[i]);
+         /* Update the dirty region.  */
+         if (region_start > screen->line)
+           {
+             region_start = screen->line;
+             region_column = screen->column;
+           }
+         for (i = 0; i < screen->nterms; i++)
+           if (orig_num[i] != new_num[i])
+             {
+               mode[i] = ALL_LINES;
+               down[i] = 1; /* XXX not optimal.  */
+             }
+           else if (mode[i] != ALL_LINES)
+             mode[i] = SINGLE_LINE;
+         /* Move the cursor.  */
+         screen->column += size;
+         screen->real_column = screen->column;
+         for (i = 0; i < screen->nterms; i++)
+           {
+             screen->terms[i].x += size;
+             screen->terms[i].y += screen->terms[i].x
+               / grub_term_entry_width (screen->terms[i].term);
+             screen->terms[i].x
+               %= grub_term_entry_width (screen->terms[i].term);
+           }
+         s = p;
+       }
+     }
+   if (update)
+     for (i = 0; i < screen->nterms; i++)
+       update_screen (screen, &screen->terms[i],
+                    region_start, region_column, 0, down[i], mode[i]);
+   return 1;
+ }
+ /* Release the resource allocated for SCREEN.  */
+ static void
+ destroy_screen (struct screen *screen)
+ {
+   int i;
+   if (screen->lines)
+     for (i = 0; i < screen->num_lines; i++)
+       {
+       struct line *linep = screen->lines + i;
+       if (linep)
+         grub_free (linep->buf);
+       }
+   grub_free (screen->killed_text);
+   grub_free (screen->lines);
+   grub_free (screen->terms);
+   grub_free (screen);
+ }
+ /* Make a new screen.  */
+ static struct screen *
+ make_screen (grub_menu_entry_t entry)
+ {
+   struct screen *screen;
+   unsigned i;
+   /* Initialize the screen.  */
+   screen = grub_zalloc (sizeof (*screen));
+   if (! screen)
+     return 0;
+   screen->num_lines = 1;
+   screen->lines = grub_malloc (sizeof (struct line));
+   if (! screen->lines)
+     goto fail;
+   /* Initialize the first line which must be always present.  */
+   if (! init_line (screen->lines))
+     goto fail;
+   insert_string (screen, (char *) entry->sourcecode, 0);
+   /* Reset the cursor position.  */
+   screen->column = 0;
+   screen->real_column = 0;
+   screen->line = 0;
+   for (i = 0; i < screen->nterms; i++)
+     {
+       screen->terms[i].x = 0;
+       screen->terms[i].y = 0;
+     }
+   return screen;
+  fail:
+   destroy_screen (screen);
+   return 0;
+ }
+ static int
+ forward_char (struct screen *screen, int update)
+ {
+   struct line *linep;
+   unsigned i;
+   linep = screen->lines + screen->line;
+   if (screen->column < linep->len)
+     {
+       screen->column++;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].x++;
+         if (screen->terms[i].x
+             == grub_term_entry_width (screen->terms[i].term))
+           {
+             screen->terms[i].x = 0;
+             screen->terms[i].y++;
+           }
+       }
+     }
+   else if (screen->num_lines > screen->line + 1)
+     {
+       screen->column = 0;
+       screen->line++;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].x = 0;
+         screen->terms[i].y++;
+       }
+     }
+   screen->real_column = screen->column;
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ backward_char (struct screen *screen, int update)
+ {
+   unsigned i;
+   if (screen->column > 0)
+     {
+       screen->column--;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].x--;
+         if (screen->terms[i].x == -1)
+           {
+             screen->terms[i].x
+               = grub_term_entry_width (screen->terms[i].term) - 1;
+             screen->terms[i].y--;
+           }
+       }
+     }
+   else if (screen->line > 0)
+     {
+       struct line *linep;
+       screen->line--;
+       linep = screen->lines + screen->line;
+       screen->column = linep->len;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].x = screen->column
+           % grub_term_entry_width (screen->terms[i].term);
+         screen->terms[i].y--;
+       }
+     }
+   screen->real_column = screen->column;
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ previous_line (struct screen *screen, int update)
+ {
+   unsigned i;
+   if (screen->line > 0)
+     {
+       struct line *linep;
+       int col;
+       /* How many physical lines from the current position
+        to the first physical line?  */
+       col = screen->column;
+       screen->line--;
+       linep = screen->lines + screen->line;
+       if (linep->len < screen->real_column)
+       screen->column = linep->len;
+       else
+       screen->column = screen->real_column;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         int dy;
+         dy = col / grub_term_entry_width (screen->terms[i].term);
+         /* How many physical lines from the current position
+            to the last physical line?  */
+         dy += (linep->len / grub_term_entry_width (screen->terms[i].term)
+                - screen->column
+                / grub_term_entry_width (screen->terms[i].term));
+       
+         screen->terms[i].y -= dy + 1;
+         screen->terms[i].x
+           = screen->column % grub_term_entry_width (screen->terms[i].term);
+       }
+     }
+   else
+     {
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].y
+           -= screen->column / grub_term_entry_width (screen->terms[i].term);
+         screen->terms[i].x = 0;
+       }
+       screen->column = 0;
+     }
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ next_line (struct screen *screen, int update)
+ {
+   unsigned i;
+   if (screen->line < screen->num_lines - 1)
+     {
+       struct line *linep;
+       int l1, c1;
+       /* How many physical lines from the current position
+        to the last physical line?  */
+       linep = screen->lines + screen->line;
+       l1 = linep->len;
+       c1 = screen->column;
+       screen->line++;
+       linep++;
+       if (linep->len < screen->real_column)
+       screen->column = linep->len;
+       else
+       screen->column = screen->real_column;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         int dy;
+         dy = l1 / grub_term_entry_width (screen->terms[i].term)
+           - c1 / grub_term_entry_width (screen->terms[i].term);
+         /* How many physical lines from the current position
+            to the first physical line?  */
+         dy += screen->column / grub_term_entry_width (screen->terms[i].term);
+         screen->terms[i].y += dy + 1;
+         screen->terms[i].x = screen->column
+           % grub_term_entry_width (screen->terms[i].term);
+       }
+     }
+   else
+     {
+       struct line *linep;
+       int l, s;
+       
+       linep = screen->lines + screen->line;
+       l = linep->len;
+       s = screen->column;
+       screen->column = linep->len;
+       for (i = 0; i < screen->nterms; i++)
+       {
+         screen->terms[i].y 
+           += (l / grub_term_entry_width (screen->terms[i].term)
+               -  s / grub_term_entry_width (screen->terms[i].term));
+         screen->terms[i].x
+           = screen->column % grub_term_entry_width (screen->terms[i].term);
+       }
+     }
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ beginning_of_line (struct screen *screen, int update)
+ {
+   unsigned i;
+   int col;
+   
+   col = screen->column;
+   screen->column = screen->real_column = 0;
+   for (i = 0; i < screen->nterms; i++)
+     {
+       screen->terms[i].x = 0;
+       screen->terms[i].y -= col / grub_term_entry_width (screen->terms[i].term);
+     }
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ end_of_line (struct screen *screen, int update)
+ {
+   struct line *linep;
+   unsigned i;
+   int col;
+   linep = screen->lines + screen->line;
+   col = screen->column;
+   screen->column = screen->real_column = linep->len;
+   for (i = 0; i < screen->nterms; i++)
+     {
+       screen->terms[i].y 
+       += (linep->len / grub_term_entry_width (screen->terms->term)
+           - col / grub_term_entry_width (screen->terms->term));
+       screen->terms[i].x
+       = screen->column % grub_term_entry_width (screen->terms->term);
+     }
+   if (update)
+     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
+   return 1;
+ }
+ static int
+ delete_char (struct screen *screen, int update)
+ {
+   struct line *linep;
+   int start = screen->num_lines;
+   int column = 0;
+   linep = screen->lines + screen->line;
+   if (linep->len > screen->column)
+     {
+       int orig_num[screen->nterms], new_num;
+       unsigned i;
+       for (i = 0; i < screen->nterms; i++)
+       orig_num[i] = get_logical_num_lines (linep, &screen->terms[i]);
+       grub_memmove (linep->buf + screen->column,
+                   linep->buf + screen->column + 1,
+                   linep->len - screen->column - 1);
+       linep->len--;
+       start = screen->line;
+       column = screen->column;
+       screen->real_column = screen->column;
+       if (update)
+       {
+         for (i = 0; i < screen->nterms; i++)
+           {
+             new_num = get_logical_num_lines (linep, &screen->terms[i]);
+             if (orig_num[i] != new_num)
+               update_screen (screen, &screen->terms[i],
+                              start, column, 0, 0, ALL_LINES);
+             else
+               update_screen (screen, &screen->terms[i],
+                              start, column, 0, 0, SINGLE_LINE);
+           }
+       }
+     }
+   else if (screen->num_lines > screen->line + 1)
+     {
+       struct line *next_linep;
+       next_linep = linep + 1;
+       if (! ensure_space (linep, next_linep->len))
+       return 0;
+       grub_memmove (linep->buf + linep->len, next_linep->buf, next_linep->len);
+       linep->len += next_linep->len;
+       grub_free (next_linep->buf);
+       grub_memmove (next_linep,
+                   next_linep + 1,
+                   (screen->num_lines - screen->line - 2)
+                   * sizeof (struct line));
+       screen->num_lines--;
+       start = screen->line;
+       column = screen->column;
+       screen->real_column = screen->column;
+       if (update)
+       update_screen_all (screen, start, column, 0, 1, ALL_LINES);
+     }
+   return 1;
+ }
+ static int
+ backward_delete_char (struct screen *screen, int update)
+ {
+   int saved_column;
+   int saved_line;
+   saved_column = screen->column;
+   saved_line = screen->line;
+   if (! backward_char (screen, 0))
+     return 0;
+   if (saved_column != screen->column || saved_line != screen->line)
+     if (! delete_char (screen, update))
+       return 0;
+   return 1;
+ }
+ static int
+ kill_line (struct screen *screen, int continuous, int update)
+ {
+   struct line *linep;
+   char *p;
+   int size;
+   int offset;
+   p = screen->killed_text;
+   if (! continuous && p)
+     p[0] = '\0';
+   linep = screen->lines + screen->line;
+   size = linep->len - screen->column;
+   if (p)
+     offset = grub_strlen (p);
+   else
+     offset = 0;
+   if (size > 0)
+     {
+       int orig_num[screen->nterms], new_num;
+       unsigned i;
+       p = grub_realloc (p, offset + size + 1);
+       if (! p)
+       return 0;
+       grub_memmove (p + offset, linep->buf + screen->column, size);
+       p[offset + size - 1] = '\0';
+       screen->killed_text = p;
+       for (i = 0; i < screen->nterms; i++)
+       orig_num[i] = get_logical_num_lines (linep, &screen->terms[i]);
+       linep->len = screen->column;
+       if (update)
+       {
+         new_num = get_logical_num_lines (linep, &screen->terms[i]);
+         for (i = 0; i < screen->nterms; i++)
+           {
+             if (orig_num[i] != new_num)
+               update_screen (screen, &screen->terms[i],
+                              screen->line, screen->column, 0, 1, ALL_LINES);
+             else
+               update_screen (screen, &screen->terms[i],
+                              screen->line, screen->column, 0, 0, SINGLE_LINE);
+           }
+       }
+     }
+   else if (screen->line + 1 < screen->num_lines)
+     {
+       p = grub_realloc (p, offset + 1 + 1);
+       if (! p)
+       return 0;
+       p[offset] = '\n';
+       p[offset + 1] = '\0';
+       screen->killed_text = p;
+       return delete_char (screen, update);
+     }
+   return 1;
+ }
+ static int
+ yank (struct screen *screen, int update)
+ {
+   if (screen->killed_text)
+     return insert_string (screen, screen->killed_text, update);
+   return 1;
+ }
+ static int
+ open_line (struct screen *screen, int update)
+ {
+   int saved_y[screen->nterms];
+   unsigned i;
+   for (i = 0; i < screen->nterms; i++)
+     saved_y[i] = screen->terms[i].y;
+   if (! insert_string (screen, "\n", 0))
+     return 0;
+   if (! backward_char (screen, 0))
+     return 0;
+   for (i = 0; i < screen->nterms; i++)
+     screen->terms[i].y = saved_y[i];
+   if (update)
+     update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES);
+   return 1;
+ }
+ /* A completion hook to print items.  */
+ static void
+ store_completion (const char *item, grub_completion_type_t type,
+                 int count __attribute__ ((unused)))
+ {
+   char *p;
+   completion_type = type;
+   /* Make sure that the completion buffer has enough room.  */
+   if (completion_buffer.max_len < (completion_buffer.len
+                                  + (int) grub_strlen (item) + 1 + 1))
+     {
+       grub_size_t new_len;
+       new_len = completion_buffer.len + grub_strlen (item) + 80;
+       p = grub_realloc (completion_buffer.buf, new_len);
+       if (! p)
+       {
+         /* Possibly not fatal.  */
+         grub_errno = GRUB_ERR_NONE;
+         return;
+       }
+       p[completion_buffer.len] = 0;
+       completion_buffer.buf = p;
+       completion_buffer.max_len = new_len;
+     }
+   p = completion_buffer.buf + completion_buffer.len;
+   if (completion_buffer.len != 0)
+     {
+       *p++ = ' ';
+       completion_buffer.len++;
+     }
+   grub_strcpy (p, item);
+   completion_buffer.len += grub_strlen (item);
+ }
+ static int
+ complete (struct screen *screen, int continuous, int update)
+ {
+   char saved_char;
+   struct line *linep;
+   int restore;
+   char *insert;
+   static int count = -1;
+   unsigned i;
+   grub_uint32_t *ucs4;
+   grub_size_t buflen;
+   grub_ssize_t ucs4len;
+   if (continuous)
+     count++;
+   else
+     count = 0;
+   completion_buffer.buf = 0;
+   completion_buffer.len = 0;
+   completion_buffer.max_len = 0;
+   linep = screen->lines + screen->line;
+   saved_char = linep->buf[screen->column];
+   linep->buf[screen->column] = '\0';
+   insert = grub_normal_do_completion (linep->buf, &restore, store_completion);
+   linep->buf[screen->column] = saved_char;
+   
+   if (completion_buffer.buf)
+     {
+       buflen = grub_strlen (completion_buffer.buf);
+       ucs4 = grub_malloc (sizeof (grub_uint32_t) * (buflen + 1));
+       
+       if (!ucs4)
+       {
+         grub_print_error ();
+         grub_errno = GRUB_ERR_NONE;
+         return 1;
+       }
+       ucs4len = grub_utf8_to_ucs4 (ucs4, buflen,
+                                  (grub_uint8_t *) completion_buffer.buf,
+                                  buflen, 0);
+       ucs4[ucs4len] = 0;
+       if (restore)
+       for (i = 0; i < screen->nterms; i++)
+         {
+           int num_sections = ((completion_buffer.len
+                                + grub_term_width (screen->terms[i].term) 
+                                - 8 - 1)
+                               / (grub_term_width (screen->terms[i].term)
+                                  - 8));
+           grub_uint32_t *endp;
+           grub_uint16_t pos;
+           grub_uint32_t *p = ucs4;
+           pos = grub_term_getxy (screen->terms[i].term);
+           grub_term_gotoxy (screen->terms[i].term, 0,
+                             grub_term_height (screen->terms[i].term) - 3);
+           screen->completion_shown = 1;
+           grub_term_gotoxy (screen->terms[i].term, 0,
+                             grub_term_height (screen->terms[i].term) - 3);
+           grub_puts_terminal ("   ", screen->terms[i].term);
+           switch (completion_type)
+             {
+             case GRUB_COMPLETION_TYPE_COMMAND:
+               grub_puts_terminal (_("Possible commands are:"),
+                                   screen->terms[i].term);
+               break;
+             case GRUB_COMPLETION_TYPE_DEVICE:
+               grub_puts_terminal (_("Possible devices are:"),
+                                   screen->terms[i].term);
+               break;
+             case GRUB_COMPLETION_TYPE_FILE:
+               grub_puts_terminal (_("Possible files are:"),
+                                   screen->terms[i].term);
+               break;
+             case GRUB_COMPLETION_TYPE_PARTITION:
+               grub_puts_terminal (_("Possible partitions are:"),
+                                   screen->terms[i].term);
+               break;
+             case GRUB_COMPLETION_TYPE_ARGUMENT:
+               grub_puts_terminal (_("Possible arguments are:"),
+                                   screen->terms[i].term);
+               break;
+             default:
+               grub_puts_terminal (_("Possible things are:"),
+                                   screen->terms[i].term);
+               break;
+             }
+           grub_puts_terminal ("\n    ", screen->terms[i].term);
+           p += (count % num_sections)
+             * (grub_term_width (screen->terms[i].term) - 8);
+           endp = p + (grub_term_width (screen->terms[i].term) - 8);
+           if (p != ucs4)
+             grub_putcode (GRUB_UNICODE_LEFTARROW, screen->terms[i].term);
+           else
+             grub_putcode (' ', screen->terms[i].term);
+           grub_print_ucs4 (p, ucs4 + ucs4len < endp ? ucs4 + ucs4len : endp,
+                            0, 0, screen->terms[i].term);
+           if (ucs4 + ucs4len > endp)
+             grub_putcode (GRUB_UNICODE_RIGHTARROW, screen->terms[i].term);
+           grub_term_gotoxy (screen->terms[i].term, pos >> 8, pos & 0xFF);
+         }
+     }
+   if (insert)
+     {
+       insert_string (screen, insert, update);
+       count = -1;
+       grub_free (insert);
+     }
+   else if (update)
+     grub_refresh ();
+   grub_free (completion_buffer.buf);
+   return 1;
+ }
+ /* Clear displayed completions.  */
+ static void
+ clear_completions (struct per_term_screen *term_screen)
+ {
+   grub_uint16_t pos;
+   unsigned i, j;
+   pos = grub_term_getxy (term_screen->term);
+   grub_term_gotoxy (term_screen->term, 0,
+                   grub_term_height (term_screen->term) - 3);
+   for (i = 0; i < 2; i++)
+     {
+       for (j = 0; j < grub_term_width (term_screen->term) - 1; j++)
+       grub_putcode (' ', term_screen->term);
+       grub_putcode ('\n', term_screen->term);
+     }
+   grub_term_gotoxy (term_screen->term, pos >> 8, pos & 0xFF);
+   grub_term_refresh (term_screen->term);
+ }
+ static void
+ clear_completions_all (struct screen *screen)
+ {
+   unsigned i;
+   for (i = 0; i < screen->nterms; i++)
+     clear_completions (&screen->terms[i]);
+ }
+ /* Execute the command list in the screen SCREEN.  */
+ static int
+ run (struct screen *screen)
+ {
+   int currline = 0;
+   char *nextline;
+   auto grub_err_t editor_getline (char **line, int cont);
+   grub_err_t editor_getline (char **line, int cont __attribute__ ((unused)))
+     {
+       struct line *linep = screen->lines + currline;
+       char *p;
+       if (currline > screen->num_lines)
+       {
+         *line = 0;
+         return 0;
+       }
+       /* Trim down space characters.  */
+       for (p = linep->buf + linep->len - 1;
+          p >= linep->buf && grub_isspace (*p);
+          p--)
+       ;
+       *++p = '\0';
+       linep->len = p - linep->buf;
+       for (p = linep->buf; grub_isspace (*p); p++)
+       ;
+       *line = grub_strdup (p);
+       currline++;
+       return 0;
+     }
+   grub_cls ();
+   grub_printf ("  ");
+   grub_printf_ (N_("Booting a command list"));
+   grub_printf ("\n\n");
+   /* Execute the script, line for line.  */
+   while (currline < screen->num_lines)
+     {
+       editor_getline (&nextline, 0);
+       if (grub_normal_parse_line (nextline, editor_getline))
+       break;
+     }
+   if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+     /* Implicit execution of boot, only if something is loaded.  */
+     grub_command_execute ("boot", 0, 0);
+   if (grub_errno != GRUB_ERR_NONE)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       grub_wait_after_message ();
+     }
+   return 1;
+ }
+ /* Edit a menu entry with an Emacs-like interface.  */
+ void
+ grub_menu_entry_run (grub_menu_entry_t entry)
+ {
+   struct screen *screen;
+   int prev_c;
+   grub_err_t err = GRUB_ERR_NONE;
+   unsigned i;
+   grub_term_output_t term;
+   err = grub_auth_check_authentication (NULL);
+   if (err)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return;
+     }
+   screen = make_screen (entry);
+   if (! screen)
+     return;
+   screen->terms = NULL;
+  refresh:
+   grub_free (screen->terms);
+   screen->nterms = 0;
+   FOR_ACTIVE_TERM_OUTPUTS(term)
+     screen->nterms++;
+   screen->terms = grub_malloc (screen->nterms * sizeof (screen->terms[0]));
+   if (!screen->terms)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return;
+     }
+   i = 0;
+   FOR_ACTIVE_TERM_OUTPUTS(term)
+   {
+     screen->terms[i].term = term;
+     screen->terms[i].x = 0;
+     screen->terms[i].y = 0;
+     i++;
+   }
+   /* Draw the screen.  */
+   for (i = 0; i < screen->nterms; i++)
+     grub_menu_init_page (0, 1, screen->terms[i].term);
+   update_screen_all (screen, 0, 0, 1, 1, ALL_LINES);
+   for (i = 0; i < screen->nterms; i++)
+     grub_term_setcursor (screen->terms[i].term, 1);
+   prev_c = '\0';
+   while (1)
+     {
 -      case 16: /* C-p */
++      int c = grub_getkey ();
+       if (screen->completion_shown)
+       {
+         clear_completions_all (screen);
+         screen->completion_shown = 0;
+       }
+       if (grub_normal_exit_level)
+       {
+         destroy_screen (screen);
+         return;
+       }
+       switch (c)
+       {
 -      case 14: /* C-n */
++      case GRUB_TERM_KEY_UP:
++      case GRUB_TERM_CTRL | 'p':
+         if (! previous_line (screen, 1))
+           goto fail;
+         break;
 -      case 6: /* C-f */
++      case GRUB_TERM_CTRL | 'n':
++      case GRUB_TERM_KEY_DOWN:
+         if (! next_line (screen, 1))
+           goto fail;
+         break;
 -      case 2: /* C-b */
++      case GRUB_TERM_CTRL | 'f':
++      case GRUB_TERM_KEY_RIGHT:
+         if (! forward_char (screen, 1))
+           goto fail;
+         break;
 -      case 1: /* C-a */
++      case GRUB_TERM_CTRL | 'b':
++      case GRUB_TERM_KEY_LEFT:
+         if (! backward_char (screen, 1))
+           goto fail;
+         break;
 -      case 5: /* C-e */
++      case GRUB_TERM_CTRL | 'a':
++      case GRUB_TERM_KEY_HOME:
+         if (! beginning_of_line (screen, 1))
+           goto fail;
+         break;
 -      case '\t': /* C-i */
++      case GRUB_TERM_CTRL | 'e':
++      case GRUB_TERM_KEY_END:
+         if (! end_of_line (screen, 1))
+           goto fail;
+         break;
 -      case 4: /* C-d */
++      case GRUB_TERM_CTRL | 'i':
++      case '\t':
+         if (! complete (screen, prev_c == c, 1))
+           goto fail;
+         break;
 -      case 8: /* C-h */
++      case GRUB_TERM_CTRL | 'd':
++      case GRUB_TERM_KEY_DC:
+         if (! delete_char (screen, 1))
+           goto fail;
+         break;
 -      case 11: /* C-k */
++      case GRUB_TERM_CTRL | 'h':
++      case '\b':
+         if (! backward_delete_char (screen, 1))
+           goto fail;
+         break;
 -      case 21: /* C-u */
++      case GRUB_TERM_CTRL | 'k':
+         if (! kill_line (screen, prev_c == c, 1))
+           goto fail;
+         break;
 -      case 25: /* C-y */
++      case GRUB_TERM_CTRL | 'u':
+         /* FIXME: What behavior is good for this key?  */
+         break;
 -      case 12: /* C-l */
++      case GRUB_TERM_CTRL | 'y':
+         if (! yank (screen, 1))
+           goto fail;
+         break;
 -      case 15: /* C-o */
++      case GRUB_TERM_CTRL | 'l':
+         /* FIXME: centering.  */
+         goto refresh;
 -      case 3: /* C-c */
++      case GRUB_TERM_CTRL | 'o':
+         if (! open_line (screen, 1))
+           goto fail;
+         break;
+       case '\n':
+       case '\r':
+         if (! insert_string (screen, "\n", 1))
+           goto fail;
+         break;
+       case '\e':
+         destroy_screen (screen);
+         return;
 -      case 24: /* C-x */
++      case GRUB_TERM_CTRL | 'c':
++      case GRUB_TERM_KEY_F2:
+         grub_cmdline_run (1);
+         goto refresh;
 -      case 18: /* C-r */
 -      case 19: /* C-s */
 -      case 20: /* C-t */
++      case GRUB_TERM_CTRL | 'x':
++      case GRUB_TERM_KEY_F10:
+         {
+           int chars_before = grub_normal_get_char_counter ();
+           run (screen);
+           if (chars_before != grub_normal_get_char_counter ())
+             grub_wait_after_message ();
+         }
+         goto refresh;
++      case GRUB_TERM_CTRL | 'r':
++      case GRUB_TERM_CTRL | 's':
++      case GRUB_TERM_CTRL | 't':
+         /* FIXME */
+         break;
+       default:
+         if (grub_isprint (c))
+           {
+             char buf[2];
+             buf[0] = c;
+             buf[1] = '\0';
+             if (! insert_string (screen, buf, 1))
+               goto fail;
+           }
+         break;
+       }
+       prev_c = c;
+     }
+  fail:
+   destroy_screen (screen);
+   grub_cls ();
+   grub_print_error ();
+   grub_errno = GRUB_ERR_NONE;
+   grub_xputs ("\n");
+   grub_printf_ (N_("Press any key to continue..."));
+   (void) grub_getkey ();
+ }
index 0000000000000000000000000000000000000000,3e3e7e2fac65981cd52d8a9267bf7e24947111ae..fc4a8919653799a0911a4bc9ece5b52ce814bc0d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,459 +1,452 @@@
 -#ifdef GRUB_MACHINE_EFI
+ /* menu_text.c - Basic text menu implementation.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/normal.h>
+ #include <grub/term.h>
+ #include <grub/misc.h>
+ #include <grub/loader.h>
+ #include <grub/mm.h>
+ #include <grub/time.h>
+ #include <grub/env.h>
+ #include <grub/menu_viewer.h>
+ #include <grub/i18n.h>
+ #include <grub/charset.h>
+ static grub_uint8_t grub_color_menu_normal;
+ static grub_uint8_t grub_color_menu_highlight;
+ struct menu_viewer_data
+ {
+   int first, offset;
+   grub_menu_t menu;
+   struct grub_term_output *term;
+ };
+ grub_ssize_t
+ grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
+                    struct grub_term_output *term)
+ {
+   grub_ssize_t width = 0;
+   while (str < last_position)
+     {
+       struct grub_unicode_glyph glyph;
+       str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
+       width += grub_term_getcharwidth (term, &glyph);
+     }
+   return width;
+ }
+ void
+ grub_print_message_indented (const char *msg, int margin_left, int margin_right,
+                            struct grub_term_output *term)
+ {
+   grub_uint32_t *unicode_msg;
+   grub_uint32_t *last_position;
+   int msg_len;
+   msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position);
+   if (msg_len < 0)
+     {
+       return;
+     }
+   grub_print_ucs4 (unicode_msg, last_position, margin_left, margin_right, term);
+   grub_free (unicode_msg);
+ }
+ static void
+ draw_border (struct grub_term_output *term)
+ {
+   unsigned i;
+   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+   grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
+   grub_putcode (GRUB_UNICODE_CORNER_UL, term);
+   for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
+     grub_putcode (GRUB_UNICODE_HLINE, term);
+   grub_putcode (GRUB_UNICODE_CORNER_UR, term);
+   for (i = 0; i < (unsigned) grub_term_num_entries (term); i++)
+     {
+       grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
+       grub_putcode (GRUB_UNICODE_VLINE, term);
+       grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term)
+                       - 1,
+                       GRUB_TERM_TOP_BORDER_Y + i + 1);
+       grub_putcode (GRUB_UNICODE_VLINE, term);
+     }
+   grub_term_gotoxy (term, GRUB_TERM_MARGIN,
+                   GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term) + 1);
+   grub_putcode (GRUB_UNICODE_CORNER_LL, term);
+   for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
+     grub_putcode (GRUB_UNICODE_HLINE, term);
+   grub_putcode (GRUB_UNICODE_CORNER_LR, term);
+   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+   grub_term_gotoxy (term, GRUB_TERM_MARGIN,
+                   (GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term)
+                    + GRUB_TERM_MARGIN + 1));
+ }
+ static void
+ print_message (int nested, int edit, struct grub_term_output *term)
+ {
+   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+   if (edit)
+     {
+       grub_putcode ('\n', term);
 -supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \
 -F4 for a command-line or ESC to discard edits and return to the GRUB menu."),
 -                                   STANDARD_MARGIN, STANDARD_MARGIN, term);
 -#else
 -      grub_print_message_indented (_("Minimum Emacs-like screen editing is \
 -supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \
+       grub_print_message_indented (_("Minimum Emacs-like screen editing is \
 -#endif
++supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
+ command-line or ESC to discard edits and return to the GRUB menu."),
+                                    STANDARD_MARGIN, STANDARD_MARGIN, term);
+     }
+   else
+     {
+       const char *msg = _("Use the %C and %C keys to select which "
+                         "entry is highlighted.\n");
+       char *msg_translated;
+       msg_translated = grub_xasprintf (msg, GRUB_UNICODE_UPARROW,
+                                      GRUB_UNICODE_DOWNARROW);
+       if (!msg_translated)
+       return;
+       grub_putcode ('\n', term);
+       grub_print_message_indented (msg_translated, STANDARD_MARGIN,
+                                  STANDARD_MARGIN, term);
+       grub_free (msg_translated);
+       if (nested)
+       {
+         grub_print_message_indented
+           (_("Press enter to boot the selected OS, "
+              "\'e\' to edit the commands before booting "
+              "or \'c\' for a command-line. ESC to return previous menu.\n"),
+            STANDARD_MARGIN, STANDARD_MARGIN, term);
+       }
+       else
+       {
+         grub_print_message_indented
+           (_("Press enter to boot the selected OS, "
+              "\'e\' to edit the commands before booting "
+              "or \'c\' for a command-line.\n"),
+            STANDARD_MARGIN, STANDARD_MARGIN, term);
+       }       
+     }
+ }
+ static void
+ print_entry (int y, int highlight, grub_menu_entry_t entry,
+            struct grub_term_output *term)
+ {
+   int x;
+   const char *title;
+   grub_size_t title_len;
+   grub_ssize_t len;
+   grub_uint32_t *unicode_title;
+   grub_ssize_t i;
+   grub_uint8_t old_color_normal, old_color_highlight;
+   title = entry ? entry->title : "";
+   title_len = grub_strlen (title);
+   unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
+   if (! unicode_title)
+     /* XXX How to show this error?  */
+     return;
+   len = grub_utf8_to_ucs4 (unicode_title, title_len,
+                            (grub_uint8_t *) title, -1, 0);
+   if (len < 0)
+     {
+       /* It is an invalid sequence.  */
+       grub_free (unicode_title);
+       return;
+     }
+   grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
+   grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
+   grub_term_setcolorstate (term, highlight
+                          ? GRUB_TERM_COLOR_HIGHLIGHT
+                          : GRUB_TERM_COLOR_NORMAL);
+   grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
+   int last_printed = 0;
+   for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
+        x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
+                 - GRUB_TERM_MARGIN);)
+     {
+       if (i < len
+         && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
+                        - GRUB_TERM_MARGIN - 1))
+       {
+         grub_ssize_t width;
+         struct grub_unicode_glyph glyph;
+         i += grub_unicode_aglomerate_comb (unicode_title + i,
+                                            len - i, &glyph);
+         width = grub_term_getcharwidth (term, &glyph);
+         grub_free (glyph.combining);
+         if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X 
+                                + grub_term_border_width (term)
+                                - GRUB_TERM_MARGIN - 1))
+           last_printed = i;
+         x += width;
+       }
+       else
+       break;
+     }
+   grub_print_ucs4 (unicode_title,
+                  unicode_title + last_printed, 0, 0, term);
+   if (last_printed != len)
+     {
+       grub_putcode (GRUB_UNICODE_RIGHTARROW, term);
+       struct grub_unicode_glyph pseudo_glyph = {
+       .base = GRUB_UNICODE_RIGHTARROW,
+       .variant = 0,
+       .attributes = 0,
+       .ncomb = 0,
+       .combining = 0,
+       .estimated_width = 1
+       };
+       x += grub_term_getcharwidth (term, &pseudo_glyph);
+     }
+   for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
+                   - GRUB_TERM_MARGIN); x++)
+     grub_putcode (' ', term);
+   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+   grub_putcode (' ', term);
+   grub_term_gotoxy (term, grub_term_cursor_x (term), y);
+   grub_term_setcolor (term, old_color_normal, old_color_highlight);
+   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+   grub_free (unicode_title);
+ }
+ static void
+ print_entries (grub_menu_t menu, int first, int offset,
+              struct grub_term_output *term)
+ {
+   grub_menu_entry_t e;
+   int i;
+   grub_term_gotoxy (term,
+                   GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term),
+                   GRUB_TERM_FIRST_ENTRY_Y);
+   if (first)
+     grub_putcode (GRUB_UNICODE_UPARROW, term);
+   else
+     grub_putcode (' ', term);
+   e = grub_menu_get_entry (menu, first);
+   for (i = 0; i < grub_term_num_entries (term); i++)
+     {
+       print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e, term);
+       if (e)
+       e = e->next;
+     }
+   grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X
+                   + grub_term_border_width (term),
+                   GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term));
+   if (e)
+     grub_putcode (GRUB_UNICODE_DOWNARROW, term);
+   else
+     grub_putcode (' ', term);
+   grub_term_gotoxy (term, grub_term_cursor_x (term),
+                   GRUB_TERM_FIRST_ENTRY_Y + offset);
+ }
+ /* Initialize the screen.  If NESTED is non-zero, assume that this menu
+    is run from another menu or a command-line. If EDIT is non-zero, show
+    a message for the menu entry editor.  */
+ void
+ grub_menu_init_page (int nested, int edit,
+                    struct grub_term_output *term)
+ {
+   grub_uint8_t old_color_normal, old_color_highlight;
+   grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
+   /* By default, use the same colors for the menu.  */
+   grub_color_menu_normal = old_color_normal;
+   grub_color_menu_highlight = old_color_highlight;
+   /* Then give user a chance to replace them.  */
+   grub_parse_color_name_pair (&grub_color_menu_normal,
+                             grub_env_get ("menu_color_normal"));
+   grub_parse_color_name_pair (&grub_color_menu_highlight,
+                             grub_env_get ("menu_color_highlight"));
+   grub_normal_init_page (term);
+   grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
+   draw_border (term);
+   grub_term_setcolor (term, old_color_normal, old_color_highlight);
+   print_message (nested, edit, term);
+ }
+ static void
+ menu_text_print_timeout (int timeout, void *dataptr)
+ {
+   const char *msg =
+     _("The highlighted entry will be executed automatically in %ds.");
+   struct menu_viewer_data *data = dataptr;
+   char *msg_translated;
+   int posx;
+   grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
+   msg_translated = grub_xasprintf (msg, timeout);
+   if (!msg_translated)
+     {
+       grub_print_error ();
+       grub_errno = GRUB_ERR_NONE;
+       return;
+     }
+   grub_print_message_indented (msg_translated, 3, 0, data->term);
+  
+   posx = grub_term_getxy (data->term) >> 8;
+   grub_print_spaces (data->term, grub_term_width (data->term) - posx - 1);
+   grub_term_gotoxy (data->term,
+                   grub_term_cursor_x (data->term),
+                   GRUB_TERM_FIRST_ENTRY_Y + data->offset);
+   grub_term_refresh (data->term);
+ }
+ static void
+ menu_text_set_chosen_entry (int entry, void *dataptr)
+ {
+   struct menu_viewer_data *data = dataptr;
+   int oldoffset = data->offset;
+   int complete_redraw = 0;
+   data->offset = entry - data->first;
+   if (data->offset > grub_term_num_entries (data->term) - 1)
+     {
+       data->first = entry - (grub_term_num_entries (data->term) - 1);
+       data->offset = grub_term_num_entries (data->term) - 1;
+       complete_redraw = 1;
+     }
+   if (data->offset < 0)
+     {
+       data->offset = 0;
+       data->first = entry;
+       complete_redraw = 1;
+     }
+   if (complete_redraw)
+     print_entries (data->menu, data->first, data->offset, data->term);
+   else
+     {
+       print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0,
+                  grub_menu_get_entry (data->menu, data->first + oldoffset),
+                  data->term);
+       print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1,
+                  grub_menu_get_entry (data->menu, data->first + data->offset),
+                  data->term);
+     }
+   grub_term_refresh (data->term);
+ }
+ static void
+ menu_text_fini (void *dataptr)
+ {
+   struct menu_viewer_data *data = dataptr;
+   grub_term_setcursor (data->term, 1);
+   grub_term_cls (data->term);
+ }
+ static void
+ menu_text_clear_timeout (void *dataptr)
+ {
+   struct menu_viewer_data *data = dataptr;
+   grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
+   grub_print_spaces (data->term, grub_term_width (data->term) - 1);
+   grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
+                   GRUB_TERM_FIRST_ENTRY_Y + data->offset);
+   grub_term_refresh (data->term);
+ }
+ grub_err_t 
+ grub_menu_try_text (struct grub_term_output *term, 
+                   int entry, grub_menu_t menu, int nested)
+ {
+   struct menu_viewer_data *data;
+   struct grub_menu_viewer *instance;
+   instance = grub_zalloc (sizeof (*instance));
+   if (!instance)
+     return grub_errno;
+   data = grub_zalloc (sizeof (*data));
+   if (!data)
+     {
+       grub_free (instance);
+       return grub_errno;
+     }
+   data->term = term;
+   instance->data = data;
+   instance->set_chosen_entry = menu_text_set_chosen_entry;
+   instance->print_timeout = menu_text_print_timeout;
+   instance->clear_timeout = menu_text_clear_timeout;
+   instance->fini = menu_text_fini;
+   data->menu = menu;
+   data->offset = entry;
+   data->first = 0;
+   if (data->offset > grub_term_num_entries (data->term) - 1)
+     {
+       data->first = data->offset - (grub_term_num_entries (data->term) - 1);
+       data->offset = grub_term_num_entries (data->term) - 1;
+     }
+   grub_term_setcursor (data->term, 0);
+   grub_menu_init_page (nested, 0, data->term);
+   print_entries (menu, data->first, data->offset, data->term);
+   grub_term_refresh (data->term);
+   grub_menu_register_viewer (instance);
+   return GRUB_ERR_NONE;
+ }
index 0000000000000000000000000000000000000000,25f32a0a5bc8621d49ccea5efcb2d524f6af018b..1b130bd62cce2fab35730cd13586792e91b55ad7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,297 +1,622 @@@
 -static int pending_key = -1;
 -
 -#define KEYBOARD_STATUS_SHIFT_L               (1 << 0)
 -#define KEYBOARD_STATUS_SHIFT_R               (1 << 1)
 -#define KEYBOARD_STATUS_ALT_L         (1 << 2)
 -#define KEYBOARD_STATUS_ALT_R         (1 << 3)
 -#define KEYBOARD_STATUS_CTRL_L                (1 << 4)
 -#define KEYBOARD_STATUS_CTRL_R                (1 << 5)
 -#define KEYBOARD_STATUS_CAPS_LOCK     (1 << 6)
 -#define KEYBOARD_STATUS_NUM_LOCK      (1 << 7)
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/at_keyboard.h>
+ #include <grub/cpu/at_keyboard.h>
+ #include <grub/cpu/io.h>
+ #include <grub/misc.h>
+ #include <grub/term.h>
++#include <grub/keyboard_layouts.h>
++#include <grub/time.h>
++#include <grub/loader.h>
+ static short at_keyboard_status = 0;
 -static char keyboard_map[128] =
 -{
 -  '\0', GRUB_TERM_ESC, '1', '2', '3', '4', '5', '6',
 -  '7', '8', '9', '0', '-', '=', GRUB_TERM_BACKSPACE, GRUB_TERM_TAB,
 -  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
 -  'o', 'p', '[', ']', '\n', '\0', 'a', 's',
 -  'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
 -  '\'', '`', '\0', '\\', 'z', 'x', 'c', 'v',
 -  'b', 'n', 'm', ',', '.', '/', '\0', '*',
 -  '\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0',
 -  '\0', '\0', '\0', '\0', '\0', '\0', '\0', GRUB_TERM_HOME,
 -  GRUB_TERM_UP, GRUB_TERM_NPAGE, '-', GRUB_TERM_LEFT, '\0', GRUB_TERM_RIGHT, '+', GRUB_TERM_END,
 -  GRUB_TERM_DOWN, GRUB_TERM_PPAGE, '\0', GRUB_TERM_DC, '\0', '\0', '\0', '\0',
 -  '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
 -  '\0', '\0', '\0', '\0', '\0', OLPC_UP, OLPC_DOWN, OLPC_LEFT,
 -  OLPC_RIGHT
 -};
 -
 -static char keyboard_map_shift[128] =
++static int e0_received = 0;
++static int f0_received = 0;
+ static grub_uint8_t led_status;
+ #define KEYBOARD_LED_SCROLL           (1 << 0)
+ #define KEYBOARD_LED_NUM              (1 << 1)
+ #define KEYBOARD_LED_CAPS             (1 << 2)
 -  '\0', '\0', '!', '@', '#', '$', '%', '^',
 -  '&', '*', '(', ')', '_', '+', '\0', '\0',
 -  'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
 -  'O', 'P', '{', '}', '\n', '\0', 'A', 'S',
 -  'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
 -  '\"', '~', '\0', '|', 'Z', 'X', 'C', 'V',
 -  'B', 'N', 'M', '<', '>', '?'
 -};
++static grub_uint8_t grub_keyboard_controller_orig;
++static grub_uint8_t grub_keyboard_orig_set;
++static grub_uint8_t current_set; 
++
++static const grub_uint8_t set1_mapping[128] =
++  {
++    /* 0x00 */ 0 /* Unused  */,               GRUB_KEYBOARD_KEY_ESCAPE, 
++    /* 0x02 */ GRUB_KEYBOARD_KEY_1,           GRUB_KEYBOARD_KEY_2, 
++    /* 0x04 */ GRUB_KEYBOARD_KEY_3,           GRUB_KEYBOARD_KEY_4, 
++    /* 0x06 */ GRUB_KEYBOARD_KEY_5,           GRUB_KEYBOARD_KEY_6, 
++    /* 0x08 */ GRUB_KEYBOARD_KEY_7,           GRUB_KEYBOARD_KEY_8, 
++    /* 0x0a */ GRUB_KEYBOARD_KEY_9,           GRUB_KEYBOARD_KEY_0, 
++    /* 0x0c */ GRUB_KEYBOARD_KEY_DASH,        GRUB_KEYBOARD_KEY_EQUAL, 
++    /* 0x0e */ GRUB_KEYBOARD_KEY_BACKSPACE,   GRUB_KEYBOARD_KEY_TAB, 
++    /* 0x10 */ GRUB_KEYBOARD_KEY_Q,           GRUB_KEYBOARD_KEY_W, 
++    /* 0x12 */ GRUB_KEYBOARD_KEY_E,           GRUB_KEYBOARD_KEY_R, 
++    /* 0x14 */ GRUB_KEYBOARD_KEY_T,           GRUB_KEYBOARD_KEY_Y, 
++    /* 0x16 */ GRUB_KEYBOARD_KEY_U,           GRUB_KEYBOARD_KEY_I, 
++    /* 0x18 */ GRUB_KEYBOARD_KEY_O,           GRUB_KEYBOARD_KEY_P, 
++    /* 0x1a */ GRUB_KEYBOARD_KEY_LBRACKET,    GRUB_KEYBOARD_KEY_RBRACKET, 
++    /* 0x1c */ GRUB_KEYBOARD_KEY_ENTER,       GRUB_KEYBOARD_KEY_LEFT_CTRL, 
++    /* 0x1e */ GRUB_KEYBOARD_KEY_A,           GRUB_KEYBOARD_KEY_S, 
++    /* 0x20 */ GRUB_KEYBOARD_KEY_D,           GRUB_KEYBOARD_KEY_F, 
++    /* 0x22 */ GRUB_KEYBOARD_KEY_G,           GRUB_KEYBOARD_KEY_H, 
++    /* 0x24 */ GRUB_KEYBOARD_KEY_J,           GRUB_KEYBOARD_KEY_K, 
++    /* 0x26 */ GRUB_KEYBOARD_KEY_L,           GRUB_KEYBOARD_KEY_SEMICOLON, 
++    /* 0x28 */ GRUB_KEYBOARD_KEY_DQUOTE,      GRUB_KEYBOARD_KEY_RQUOTE, 
++    /* 0x2a */ GRUB_KEYBOARD_KEY_LEFT_SHIFT,  GRUB_KEYBOARD_KEY_BACKSLASH, 
++    /* 0x2c */ GRUB_KEYBOARD_KEY_Z,           GRUB_KEYBOARD_KEY_X, 
++    /* 0x2e */ GRUB_KEYBOARD_KEY_C,           GRUB_KEYBOARD_KEY_V, 
++    /* 0x30 */ GRUB_KEYBOARD_KEY_B,           GRUB_KEYBOARD_KEY_N, 
++    /* 0x32 */ GRUB_KEYBOARD_KEY_M,           GRUB_KEYBOARD_KEY_COMMA, 
++    /* 0x34 */ GRUB_KEYBOARD_KEY_DOT,         GRUB_KEYBOARD_KEY_SLASH, 
++    /* 0x36 */ GRUB_KEYBOARD_KEY_RIGHT_SHIFT, GRUB_KEYBOARD_KEY_NUMMUL, 
++    /* 0x38 */ GRUB_KEYBOARD_KEY_LEFT_ALT,    GRUB_KEYBOARD_KEY_SPACE, 
++    /* 0x3a */ GRUB_KEYBOARD_KEY_CAPS_LOCK,   GRUB_KEYBOARD_KEY_F1, 
++    /* 0x3c */ GRUB_KEYBOARD_KEY_F2,          GRUB_KEYBOARD_KEY_F3, 
++    /* 0x3e */ GRUB_KEYBOARD_KEY_F4,          GRUB_KEYBOARD_KEY_F5, 
++    /* 0x40 */ GRUB_KEYBOARD_KEY_F6,          GRUB_KEYBOARD_KEY_F7, 
++    /* 0x42 */ GRUB_KEYBOARD_KEY_F8,          GRUB_KEYBOARD_KEY_F9, 
++    /* 0x44 */ GRUB_KEYBOARD_KEY_F10,         GRUB_KEYBOARD_KEY_NUM_LOCK, 
++    /* 0x46 */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, GRUB_KEYBOARD_KEY_NUM7, 
++    /* 0x48 */ GRUB_KEYBOARD_KEY_NUM8,        GRUB_KEYBOARD_KEY_NUM9, 
++    /* 0x4a */ GRUB_KEYBOARD_KEY_NUMMINUS,    GRUB_KEYBOARD_KEY_NUM4, 
++    /* 0x4c */ GRUB_KEYBOARD_KEY_NUM5,        GRUB_KEYBOARD_KEY_NUM6, 
++    /* 0x4e */ GRUB_KEYBOARD_KEY_NUMPLUS,     GRUB_KEYBOARD_KEY_NUM1, 
++    /* 0x50 */ GRUB_KEYBOARD_KEY_NUM2,        GRUB_KEYBOARD_KEY_NUM3, 
++    /* 0x52 */ GRUB_KEYBOARD_KEY_NUMDOT,      GRUB_KEYBOARD_KEY_NUMDOT, 
++    /* 0x54 */ 0,                             0, 
++    /* 0x56 */ GRUB_KEYBOARD_KEY_102ND,       GRUB_KEYBOARD_KEY_F11, 
++    /* 0x58 */ GRUB_KEYBOARD_KEY_F12,         0,
++    /* 0x5a */ 0,                             0,
++    /* 0x5c */ 0,                             0,
++    /* 0x5e */ 0,                             0,
++    /* 0x60 */ 0,                             0,
++    /* 0x62 */ 0,                             0,
++    /* OLPC keys. Just mapped to normal keys.  */
++    /* 0x64 */ 0,                             GRUB_KEYBOARD_KEY_UP,
++    /* 0x66 */ GRUB_KEYBOARD_KEY_DOWN,        GRUB_KEYBOARD_KEY_LEFT,
++    /* 0x68 */ GRUB_KEYBOARD_KEY_RIGHT
++  };
++
++static const struct
+ {
 -static grub_uint8_t grub_keyboard_controller_orig;
++  grub_uint8_t from, to;
++} set1_e0_mapping[] = 
++  {
++    {0x1c, GRUB_KEYBOARD_KEY_NUMENTER},
++    {0x1d, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
++    {0x35, GRUB_KEYBOARD_KEY_NUMSLASH }, 
++    {0x38, GRUB_KEYBOARD_KEY_RIGHT_ALT},
++    {0x47, GRUB_KEYBOARD_KEY_HOME}, 
++    {0x48, GRUB_KEYBOARD_KEY_UP},
++    {0x49, GRUB_KEYBOARD_KEY_NPAGE},
++    {0x4b, GRUB_KEYBOARD_KEY_LEFT},
++    {0x4d, GRUB_KEYBOARD_KEY_RIGHT},
++    {0x4f, GRUB_KEYBOARD_KEY_END}, 
++    {0x50, GRUB_KEYBOARD_KEY_DOWN},
++    {0x51, GRUB_KEYBOARD_KEY_PPAGE}, 
++    {0x52, GRUB_KEYBOARD_KEY_INSERT},
++    {0x53, GRUB_KEYBOARD_KEY_DELETE}, 
++  };
 -  grub_outb (KEYBOARD_COMMAND_WRITE, KEYBOARD_REG_STATUS);
++static const grub_uint8_t set2_mapping[256] =
++  {
++    /* 0x00 */ 0,                             GRUB_KEYBOARD_KEY_F9,
++    /* 0x02 */ 0,                             GRUB_KEYBOARD_KEY_F5,
++    /* 0x04 */ GRUB_KEYBOARD_KEY_F3,          GRUB_KEYBOARD_KEY_F1,
++    /* 0x06 */ GRUB_KEYBOARD_KEY_F2,          GRUB_KEYBOARD_KEY_F12,
++    /* 0x08 */ 0,                             GRUB_KEYBOARD_KEY_F10,
++    /* 0x0a */ GRUB_KEYBOARD_KEY_F8,          GRUB_KEYBOARD_KEY_F6,
++    /* 0x0c */ GRUB_KEYBOARD_KEY_F4,          GRUB_KEYBOARD_KEY_TAB,
++    /* 0x0e */ GRUB_KEYBOARD_KEY_RQUOTE,      0,
++    /* 0x10 */ 0,                             GRUB_KEYBOARD_KEY_LEFT_ALT,
++    /* 0x12 */ GRUB_KEYBOARD_KEY_LEFT_SHIFT,  0,
++    /* 0x14 */ GRUB_KEYBOARD_KEY_LEFT_CTRL,   GRUB_KEYBOARD_KEY_Q,
++    /* 0x16 */ GRUB_KEYBOARD_KEY_1,           0,
++    /* 0x18 */ 0,                             0,
++    /* 0x1a */ GRUB_KEYBOARD_KEY_Z,           GRUB_KEYBOARD_KEY_S,
++    /* 0x1c */ GRUB_KEYBOARD_KEY_A,           GRUB_KEYBOARD_KEY_W,
++    /* 0x1e */ GRUB_KEYBOARD_KEY_2,           0,
++    /* 0x20 */ 0,                             GRUB_KEYBOARD_KEY_C,
++    /* 0x22 */ GRUB_KEYBOARD_KEY_X,           GRUB_KEYBOARD_KEY_D,
++    /* 0x24 */ GRUB_KEYBOARD_KEY_E,           GRUB_KEYBOARD_KEY_4,
++    /* 0x26 */ GRUB_KEYBOARD_KEY_3,           0,
++    /* 0x28 */ 0,                             GRUB_KEYBOARD_KEY_SPACE,
++    /* 0x2a */ GRUB_KEYBOARD_KEY_V,           GRUB_KEYBOARD_KEY_F,
++    /* 0x2c */ GRUB_KEYBOARD_KEY_T,           GRUB_KEYBOARD_KEY_R,
++    /* 0x2e */ GRUB_KEYBOARD_KEY_5,           0,
++    /* 0x30 */ 0,                             GRUB_KEYBOARD_KEY_N,
++    /* 0x32 */ GRUB_KEYBOARD_KEY_B,           GRUB_KEYBOARD_KEY_H,
++    /* 0x34 */ GRUB_KEYBOARD_KEY_G,           GRUB_KEYBOARD_KEY_Y,
++    /* 0x36 */ GRUB_KEYBOARD_KEY_6,           0,
++    /* 0x38 */ 0,                             0,
++    /* 0x3a */ GRUB_KEYBOARD_KEY_M,           GRUB_KEYBOARD_KEY_J,
++    /* 0x3c */ GRUB_KEYBOARD_KEY_U,           GRUB_KEYBOARD_KEY_7,
++    /* 0x3e */ GRUB_KEYBOARD_KEY_8,           0,
++    /* 0x40 */ 0,                             GRUB_KEYBOARD_KEY_COMMA,
++    /* 0x42 */ GRUB_KEYBOARD_KEY_K,           GRUB_KEYBOARD_KEY_I,
++    /* 0x44 */ GRUB_KEYBOARD_KEY_O,           GRUB_KEYBOARD_KEY_0,
++    /* 0x46 */ GRUB_KEYBOARD_KEY_9,           0,
++    /* 0x48 */ 0,                             GRUB_KEYBOARD_KEY_DOT,
++    /* 0x4a */ GRUB_KEYBOARD_KEY_SLASH,       GRUB_KEYBOARD_KEY_L,
++    /* 0x4c */ GRUB_KEYBOARD_KEY_SEMICOLON,   GRUB_KEYBOARD_KEY_P,
++    /* 0x4e */ GRUB_KEYBOARD_KEY_DASH,        0,
++    /* 0x50 */ 0,                             0,
++    /* 0x52 */ GRUB_KEYBOARD_KEY_DQUOTE,      0,
++    /* 0x54 */ GRUB_KEYBOARD_KEY_LBRACKET,    GRUB_KEYBOARD_KEY_EQUAL,
++    /* 0x56 */ 0,                             0,
++    /* 0x58 */ GRUB_KEYBOARD_KEY_CAPS_LOCK,   GRUB_KEYBOARD_KEY_RIGHT_SHIFT,
++    /* 0x5a */ GRUB_KEYBOARD_KEY_ENTER,       GRUB_KEYBOARD_KEY_RBRACKET,
++    /* 0x5c */ 0,                             GRUB_KEYBOARD_KEY_BACKSLASH,
++    /* 0x5e */ 0,                             0,
++    /* 0x60 */ 0,                             GRUB_KEYBOARD_KEY_102ND,
++    /* 0x62 */ 0,                             0,
++    /* 0x64 */ 0,                             0,
++    /* 0x66 */ GRUB_KEYBOARD_KEY_BACKSPACE,   0,
++    /* 0x68 */ 0,                             GRUB_KEYBOARD_KEY_NUM1,
++    /* 0x6a */ 0,                             GRUB_KEYBOARD_KEY_NUM4,
++    /* 0x6c */ GRUB_KEYBOARD_KEY_NUM7,        0,
++    /* 0x6e */ 0,                             0,
++    /* 0x70 */ GRUB_KEYBOARD_KEY_NUMDOT,      GRUB_KEYBOARD_KEY_NUM0,
++    /* 0x72 */ GRUB_KEYBOARD_KEY_NUM2,        GRUB_KEYBOARD_KEY_NUM5,
++    /* 0x74 */ GRUB_KEYBOARD_KEY_NUM6,        GRUB_KEYBOARD_KEY_NUM8,
++    /* 0x76 */ GRUB_KEYBOARD_KEY_ESCAPE,      GRUB_KEYBOARD_KEY_NUM_LOCK,
++    /* 0x78 */ GRUB_KEYBOARD_KEY_F11,         GRUB_KEYBOARD_KEY_NUMPLUS,
++    /* 0x7a */ GRUB_KEYBOARD_KEY_NUM3,        GRUB_KEYBOARD_KEY_NUMMINUS,
++    /* 0x7c */ GRUB_KEYBOARD_KEY_NUMMUL,      GRUB_KEYBOARD_KEY_NUM9,
++    /* 0x7e */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, 0,
++    /* 0x80 */ 0,                             0, 
++    /* 0x82 */ 0,                             GRUB_KEYBOARD_KEY_F7,
++  };
++
++static const struct
++{
++  grub_uint8_t from, to;
++} set2_e0_mapping[] = 
++  {
++    {0x11, GRUB_KEYBOARD_KEY_RIGHT_ALT},
++    {0x14, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
++    {0x4a, GRUB_KEYBOARD_KEY_NUMSLASH},
++    {0x5a, GRUB_KEYBOARD_KEY_NUMENTER},
++    {0x69, GRUB_KEYBOARD_KEY_END},
++    {0x6b, GRUB_KEYBOARD_KEY_LEFT},
++    {0x6c, GRUB_KEYBOARD_KEY_HOME},
++    {0x70, GRUB_KEYBOARD_KEY_INSERT},
++    {0x71, GRUB_KEYBOARD_KEY_DELETE},
++    {0x72, GRUB_KEYBOARD_KEY_DOWN},
++    {0x74, GRUB_KEYBOARD_KEY_RIGHT},
++    {0x75, GRUB_KEYBOARD_KEY_UP},
++    {0x7a, GRUB_KEYBOARD_KEY_NPAGE},
++    {0x7d, GRUB_KEYBOARD_KEY_PPAGE},
++  };
+ static void
+ keyboard_controller_wait_until_ready (void)
+ {
+   while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
+ }
++static grub_uint8_t
++wait_ack (void)
++{
++  grub_uint64_t endtime;
++  grub_uint8_t ack;
++
++  endtime = grub_get_time_ms () + 20;
++  do
++    ack = grub_inb (KEYBOARD_REG_DATA);
++  while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
++       && grub_get_time_ms () < endtime);
++  return ack;
++}
++
++static int
++at_command (grub_uint8_t data)
++{
++  unsigned i;
++  for (i = 0; i < GRUB_AT_TRIES; i++)
++    {
++      grub_uint8_t ack;
++      keyboard_controller_wait_until_ready ();
++      grub_outb (data, KEYBOARD_REG_STATUS);
++      ack = wait_ack ();
++      if (ack == GRUB_AT_NACK)
++      continue;
++      if (ack == GRUB_AT_ACK)
++      break;
++      return 0;
++    }
++  return (i != GRUB_AT_TRIES);
++}
++
+ static void
+ grub_keyboard_controller_write (grub_uint8_t c)
+ {
++  at_command (KEYBOARD_COMMAND_WRITE);
+   keyboard_controller_wait_until_ready ();
 -  grub_outb (KEYBOARD_COMMAND_READ, KEYBOARD_REG_STATUS);
+   grub_outb (c, KEYBOARD_REG_DATA);
+ }
+ static grub_uint8_t
+ grub_keyboard_controller_read (void)
+ {
++  at_command (KEYBOARD_COMMAND_READ);
+   keyboard_controller_wait_until_ready ();
 -static void
 -grub_keyboard_isr (char key)
+   return grub_inb (KEYBOARD_REG_DATA);
+ }
++static int
++write_mode (int mode)
++{
++  unsigned i;
++  for (i = 0; i < GRUB_AT_TRIES; i++)
++    {
++      grub_uint8_t ack;
++      keyboard_controller_wait_until_ready ();
++      grub_outb (0xf0, KEYBOARD_REG_DATA);
++      keyboard_controller_wait_until_ready ();
++      grub_outb (mode, KEYBOARD_REG_DATA);
++      keyboard_controller_wait_until_ready ();
++      ack = wait_ack ();
++      if (ack == GRUB_AT_NACK)
++      continue;
++      if (ack == GRUB_AT_ACK)
++      break;
++      return 0;
++    }
++
++  return (i != GRUB_AT_TRIES);
++}
++
++static int
++query_mode (void)
++{
++  grub_uint8_t ret;
++  int e;
++
++  e = write_mode (0);
++  if (!e)
++    return 0;
++
++  keyboard_controller_wait_until_ready ();
++
++  do
++    ret = grub_inb (KEYBOARD_REG_DATA);
++  while (ret == GRUB_AT_ACK);
++
++  /* QEMU translates the set even in no-translate mode.  */
++  if (ret == 0x43 || ret == 1)
++    return 1;
++  if (ret == 0x41 || ret == 2)
++    return 2;
++  if (ret == 0x3f || ret == 3)
++    return 3;
++  return 0;
++}
++
++static void
++set_scancodes (void)
++{
++  /* You must have visited computer museum. Keyboard without scancode set
++     knowledge. Assume XT. */
++  if (!grub_keyboard_orig_set)
++    {
++      grub_dprintf ("atkeyb", "No sets support assumed\n");
++      current_set = 1;
++      return;
++    }
++
++  grub_keyboard_controller_write (grub_keyboard_controller_orig
++                                & ~KEYBOARD_AT_TRANSLATE);
++
++  write_mode (2);
++  current_set = query_mode ();
++  grub_dprintf ("atkeyb", "returned set %d\n", current_set);
++  if (current_set == 2)
++    return;
++
++  write_mode (1);
++  current_set = query_mode ();
++  grub_dprintf ("atkeyb", "returned set %d\n", current_set);
++  if (current_set == 1)
++    return;
++  grub_printf ("No supported scancode set found\n");
++}
++
+ static void
+ keyboard_controller_led (grub_uint8_t leds)
+ {
+   keyboard_controller_wait_until_ready ();
+   grub_outb (0xed, KEYBOARD_REG_DATA);
+   keyboard_controller_wait_until_ready ();
+   grub_outb (leds & 0x7, KEYBOARD_REG_DATA);
+ }
++static int
++fetch_key (int *is_break)
++{
++  int was_ext = 0;
++  grub_uint8_t at_key;
++  int ret = 0;
++
++  if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
++    return -1;
++  at_key = grub_inb (KEYBOARD_REG_DATA);
++  if (at_key == 0xe0)
++    {
++      e0_received = 1;
++      return -1;
++    }
++
++  if ((current_set == 2 || current_set == 3) && at_key == 0xf0)
++    {
++      f0_received = 1;
++      return -1;
++    }
++
++  /* Setting LEDs may generate ACKs.  */
++  if (at_key == GRUB_AT_ACK)
++    return -1;
++
++  was_ext = e0_received;
++  e0_received = 0;
++
++  switch (current_set)
++    {
++    case 1:
++      *is_break = !!(at_key & 0x80);
++      if (!was_ext)
++      ret = set1_mapping[at_key & 0x7f];
++      else
++      {
++        unsigned i;
++        for (i = 0; i < ARRAY_SIZE (set1_e0_mapping); i++)
++          if (set1_e0_mapping[i].from == (at_key & 0x7f))
++            {
++              ret = set1_e0_mapping[i].to;
++              break;
++            }
++      }
++      break;
++    case 2:
++      *is_break = f0_received;
++      f0_received = 0;
++      if (!was_ext)
++      ret = set2_mapping[at_key];
++      else
++      {
++        unsigned i;
++        for (i = 0; i < ARRAY_SIZE (set2_e0_mapping); i++)
++          if (set2_e0_mapping[i].from == at_key)
++            {
++              ret = set2_e0_mapping[i].to;
++              break;
++            }
++      }       
++      break;
++    default:
++      return -1;
++    }
++  if (!ret)
++    {
++      if (was_ext)
++      grub_printf ("Unknown key 0xe0+0x%02x from set %d\n",
++                   at_key, current_set);
++      else
++      grub_printf ("Unknown key 0x%02x from set %d\n",
++                   at_key, current_set);
++      return -1;
++    }
++  return ret;
++}
++
+ /* FIXME: This should become an interrupt service routine.  For now
+    it's just used to catch events from control keys.  */
 -  char is_make = KEYBOARD_ISMAKE (key);
 -  key = KEYBOARD_SCANCODE (key);
 -  if (is_make)
++static int
++grub_keyboard_isr (grub_keyboard_key_t key, int is_break)
+ {
 -      case SHIFT_L:
 -        at_keyboard_status |= KEYBOARD_STATUS_SHIFT_L;
 -        break;
 -      case SHIFT_R:
 -        at_keyboard_status |= KEYBOARD_STATUS_SHIFT_R;
 -        break;
 -      case CTRL:
 -        at_keyboard_status |= KEYBOARD_STATUS_CTRL_L;
 -        break;
 -      case ALT:
 -        at_keyboard_status |= KEYBOARD_STATUS_ALT_L;
 -        break;
 -      default:
 -        /* Skip grub_dprintf.  */
 -        return;
++  if (!is_break)
+     switch (key)
+       {
 -      case SHIFT_L:
 -        at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_L;
 -        break;
 -      case SHIFT_R:
 -        at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_R;
 -        break;
 -      case CTRL:
 -        at_keyboard_status &= ~KEYBOARD_STATUS_CTRL_L;
 -        break;
 -      case ALT:
 -        at_keyboard_status &= ~KEYBOARD_STATUS_ALT_L;
 -        break;
 -      default:
 -        /* Skip grub_dprintf.  */
 -        return;
++      case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
++      at_keyboard_status |= GRUB_TERM_STATUS_LSHIFT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
++      at_keyboard_status |= GRUB_TERM_STATUS_RSHIFT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_LEFT_CTRL:
++      at_keyboard_status |= GRUB_TERM_STATUS_LCTRL;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
++      at_keyboard_status |= GRUB_TERM_STATUS_RCTRL;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_ALT:
++      at_keyboard_status |= GRUB_TERM_STATUS_RALT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_LEFT_ALT:
++      at_keyboard_status |= GRUB_TERM_STATUS_LALT;
++      return 1;
++      default:
++      return 0;
+       }
+   else
+     switch (key)
+       {
 -#ifdef DEBUG_AT_KEYBOARD
 -  grub_dprintf ("atkeyb", "Control key 0x%0x was %s\n", key, is_make ? "pressed" : "unpressed");
 -#endif
++      case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_LSHIFT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_RSHIFT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_LEFT_CTRL:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_LCTRL;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_RCTRL;
++      return 1;
++      case GRUB_KEYBOARD_KEY_RIGHT_ALT:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_RALT;
++      return 1;
++      case GRUB_KEYBOARD_KEY_LEFT_ALT:
++      at_keyboard_status &= ~GRUB_TERM_STATUS_LALT;
++      return 1;
++      default:
++      return 0;
+       }
 -  grub_uint8_t key;
 -  if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
+ }
+ /* If there is a raw key pending, return it; otherwise return -1.  */
+ static int
+ grub_keyboard_getkey (void)
+ {
 -  key = grub_inb (KEYBOARD_REG_DATA);
 -  /* FIXME */ grub_keyboard_isr (key);
 -  if (! KEYBOARD_ISMAKE (key))
++  int key;
++  int is_break;
++
++  key = fetch_key (&is_break);
++  if (key == -1)
++    return -1;
++
++  if (grub_keyboard_isr (key, is_break))
+     return -1;
 -  return (KEYBOARD_SCANCODE (key));
++  if (is_break)
+     return -1;
 -/* If there is a character pending, return it; otherwise return -1.  */
++  return key;
+ }
 -grub_at_keyboard_getkey_noblock (void)
++/* If there is a character pending, return it;
++   otherwise return GRUB_TERM_NO_KEY.  */
+ static int
 -  int code, key;
++grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
+ {
 -    return -1;
++  int code;
+   code = grub_keyboard_getkey ();
+   if (code == -1)
 -      case CAPS_LOCK:
 -      at_keyboard_status ^= KEYBOARD_STATUS_CAPS_LOCK;
++    return GRUB_TERM_NO_KEY;
+ #ifdef DEBUG_AT_KEYBOARD
+   grub_dprintf ("atkeyb", "Detected key 0x%x\n", key);
+ #endif
+   switch (code)
+     {
 -      key = -1;
 -      break;
 -      case NUM_LOCK:
 -      at_keyboard_status ^= KEYBOARD_STATUS_NUM_LOCK;
++      case GRUB_KEYBOARD_KEY_CAPS_LOCK:
++      at_keyboard_status ^= GRUB_TERM_STATUS_CAPS;
+       led_status ^= KEYBOARD_LED_CAPS;
+       keyboard_controller_led (led_status);
+ #ifdef DEBUG_AT_KEYBOARD
+       grub_dprintf ("atkeyb", "caps_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK));
+ #endif
 -      key = -1;
 -      break;
 -      case SCROLL_LOCK:
 -      /* For scroll lock we don't keep track of status.  Only update its led.  */
++      return GRUB_TERM_NO_KEY;
++      case GRUB_KEYBOARD_KEY_NUM_LOCK:
++      at_keyboard_status ^= GRUB_TERM_STATUS_NUM;
+       led_status ^= KEYBOARD_LED_NUM;
+       keyboard_controller_led (led_status);
+ #ifdef DEBUG_AT_KEYBOARD
+       grub_dprintf ("atkeyb", "num_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_NUM_LOCK));
+ #endif
 -      key = -1;
 -      break;
++      return GRUB_TERM_NO_KEY;
++      case GRUB_KEYBOARD_KEY_SCROLL_LOCK:
++      at_keyboard_status ^= GRUB_TERM_STATUS_SCROLL;
+       led_status ^= KEYBOARD_LED_SCROLL;
+       keyboard_controller_led (led_status);
 -      if (at_keyboard_status & (KEYBOARD_STATUS_CTRL_L | KEYBOARD_STATUS_CTRL_R))
 -        key = keyboard_map[code] - 'a' + 1;
 -      else if ((at_keyboard_status & (KEYBOARD_STATUS_SHIFT_L | KEYBOARD_STATUS_SHIFT_R))
 -          && keyboard_map_shift[code])
 -        key = keyboard_map_shift[code];
 -      else
 -        key = keyboard_map[code];
 -
 -      if (key == 0)
 -        grub_dprintf ("atkeyb", "Unknown key 0x%x detected\n", code);
 -
 -      if (at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK)
 -        {
 -          if ((key >= 'a') && (key <= 'z'))
 -            key += 'A' - 'a';
 -          else if ((key >= 'A') && (key <= 'Z'))
 -            key += 'a' - 'A';
 -        }
++      return GRUB_TERM_NO_KEY;
+       default:
 -  return key;
++      return grub_term_map_key (code, at_keyboard_status);
+     }
 -static int
 -grub_at_keyboard_checkkey (struct grub_term_input *term __attribute__ ((unused)))
+ }
 -  if (pending_key != -1)
 -    return 1;
 -
 -  pending_key = grub_at_keyboard_getkey_noblock ();
 -
 -  if (pending_key != -1)
 -    return 1;
++static grub_err_t
++grub_keyboard_controller_init (struct grub_term_input *term __attribute__ ((unused)))
+ {
 -  return -1;
++  at_keyboard_status = 0;
++  /* Drain input buffer. */
++  while (1)
++    {
++      keyboard_controller_wait_until_ready ();
++      if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
++      break;
++      keyboard_controller_wait_until_ready ();
++      grub_inb (KEYBOARD_REG_DATA);
++    }
++  grub_keyboard_controller_orig = grub_keyboard_controller_read ();
++  grub_keyboard_orig_set = query_mode ();
++  set_scancodes ();
++  keyboard_controller_led (led_status);
 -static int
 -grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
++  return GRUB_ERR_NONE;
+ }
 -  int key;
 -  if (pending_key != -1)
 -    {
 -      key = pending_key;
 -      pending_key = -1;
 -      return key;
 -    }
 -  do
 -    {
 -      key = grub_at_keyboard_getkey_noblock ();
 -    } while (key == -1);
 -  return key;
++static grub_err_t
++grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
+ {
 -grub_keyboard_controller_init (struct grub_term_input *term __attribute__ ((unused)))
++  if (grub_keyboard_orig_set)
++    write_mode (grub_keyboard_orig_set);
++  grub_keyboard_controller_write (grub_keyboard_controller_orig);
++  return GRUB_ERR_NONE;
+ }
+ static grub_err_t
 -  pending_key = -1;
 -  at_keyboard_status = 0;
 -  grub_keyboard_controller_orig = grub_keyboard_controller_read ();
 -  grub_keyboard_controller_write (grub_keyboard_controller_orig | KEYBOARD_SCANCODE_SET1);
 -  return GRUB_ERR_NONE;
++grub_at_fini_hw (int noreturn __attribute__ ((unused)))
+ {
 -grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
++  return grub_keyboard_controller_fini (NULL);
+ }
+ static grub_err_t
 -  grub_keyboard_controller_write (grub_keyboard_controller_orig);
++grub_at_restore_hw (void)
+ {
 -    .checkkey = grub_at_keyboard_checkkey,
 -    .getkey = grub_at_keyboard_getkey,
++  /* Drain input buffer. */
++  while (1)
++    {
++      keyboard_controller_wait_until_ready ();
++      if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
++      break;
++      keyboard_controller_wait_until_ready ();
++      grub_inb (KEYBOARD_REG_DATA);
++    }
++  set_scancodes ();
++  keyboard_controller_led (led_status);
++
+   return GRUB_ERR_NONE;
+ }
++
+ static struct grub_term_input grub_at_keyboard_term =
+   {
+     .name = "at_keyboard",
+     .init = grub_keyboard_controller_init,
+     .fini = grub_keyboard_controller_fini,
++    .getkey = grub_at_keyboard_getkey
+   };
+ GRUB_MOD_INIT(at_keyboard)
+ {
+   grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
++  grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw,
++                                   GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+ }
+ GRUB_MOD_FINI(at_keyboard)
+ {
++  grub_keyboard_controller_fini (NULL);
+   grub_term_unregister_input (&grub_at_keyboard_term);
+ }
index 0000000000000000000000000000000000000000,6027d0ab384996f075064d320a1ad4ef34be498d..c92f6a75ee9c5154e473d2953987b43f7970ec04
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,399 +1,296 @@@
 -static int read_key = -1;
 -
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2006,2007,2008  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/term.h>
+ #include <grub/misc.h>
+ #include <grub/types.h>
+ #include <grub/err.h>
+ #include <grub/efi/efi.h>
+ #include <grub/efi/api.h>
+ #include <grub/efi/console.h>
+ static const grub_uint8_t
+ grub_console_standard_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW,
+                                                 GRUB_EFI_BACKGROUND_BLACK);
 -static int
 -grub_console_checkkey (struct grub_term_input *term __attribute__ ((unused)))
 -{
 -  grub_efi_simple_input_interface_t *i;
 -  grub_efi_input_key_t key;
 -  grub_efi_status_t status;
 -
 -  if (grub_efi_is_finished)
 -    return 0;
 -
 -  if (read_key >= 0)
 -    return 1;
 -
 -  i = grub_efi_system_table->con_in;
 -  status = efi_call_2 (i->read_key_stroke, i, &key);
 -#if 0
 -  switch (status)
 -    {
 -    case GRUB_EFI_SUCCESS:
 -      {
 -      grub_uint16_t xy;
 -
 -      xy = grub_getxy ();
 -      grub_gotoxy (0, 0);
 -      grub_printf ("scan_code=%x,unicode_char=%x  ",
 -                   (unsigned) key.scan_code,
 -                   (unsigned) key.unicode_char);
 -      grub_gotoxy (xy >> 8, xy & 0xff);
 -      }
 -      break;
 -
 -    case GRUB_EFI_NOT_READY:
 -      //grub_printf ("not ready   ");
 -      break;
 -
 -    default:
 -      //grub_printf ("device error   ");
 -      break;
 -    }
 -#endif
+ static grub_uint32_t
+ map_char (grub_uint32_t c)
+ {
+   /* Map some unicode characters to the EFI character.  */
+   switch (c)
+     {
+     case GRUB_UNICODE_LEFTARROW:
+       c = GRUB_UNICODE_BLACK_LEFT_TRIANGLE;
+       break;
+     case GRUB_UNICODE_UPARROW:
+       c = GRUB_UNICODE_BLACK_UP_TRIANGLE;
+       break;
+     case GRUB_UNICODE_RIGHTARROW:
+       c = GRUB_UNICODE_BLACK_RIGHT_TRIANGLE;
+       break;
+     case GRUB_UNICODE_DOWNARROW:
+       c = GRUB_UNICODE_BLACK_DOWN_TRIANGLE;
+       break;
+     case GRUB_UNICODE_HLINE:
+       c = GRUB_UNICODE_LIGHT_HLINE;
+       break;
+     case GRUB_UNICODE_VLINE:
+       c = GRUB_UNICODE_LIGHT_VLINE;
+       break;
+     case GRUB_UNICODE_CORNER_UL:
+       c = GRUB_UNICODE_LIGHT_CORNER_UL;
+       break;
+     case GRUB_UNICODE_CORNER_UR:
+       c = GRUB_UNICODE_LIGHT_CORNER_UR;
+       break;
+     case GRUB_UNICODE_CORNER_LL:
+       c = GRUB_UNICODE_LIGHT_CORNER_LL;
+       break;
+     case GRUB_UNICODE_CORNER_LR:
+       c = GRUB_UNICODE_LIGHT_CORNER_LR;
+       break;
+     }
+   return c;
+ }
+ static void
+ grub_console_putchar (struct grub_term_output *term __attribute__ ((unused)),
+                     const struct grub_unicode_glyph *c)
+ {
+   grub_efi_char16_t str[2 + c->ncomb];
+   grub_efi_simple_text_output_interface_t *o;
+   unsigned i, j;
+   if (grub_efi_is_finished)
+     return;
+   o = grub_efi_system_table->con_out;
+   /* For now, do not try to use a surrogate pair.  */
+   if (c->base > 0xffff)
+     str[0] = '?';
+   else
+     str[0] = (grub_efi_char16_t)  map_char (c->base & 0xffff);
+   j = 1;
+   for (i = 0; i < c->ncomb; i++)
+     if (c->base < 0xffff)
+       str[j++] = c->combining[i].code;
+   str[j] = 0;
+   /* Should this test be cached?  */
+   if ((c->base > 0x7f || c->ncomb)
+       && efi_call_2 (o->test_string, o, str) != GRUB_EFI_SUCCESS)
+     return;
+   efi_call_2 (o->output_string, o, str);
+ }
 -  if (status == GRUB_EFI_SUCCESS)
 -    {
 -      switch (key.scan_code)
 -      {
 -      case 0x00:
 -        read_key = key.unicode_char;
 -        break;
 -      case 0x01:
 -        read_key = GRUB_TERM_UP;
 -        break;
 -      case 0x02:
 -        read_key = GRUB_TERM_DOWN;
 -        break;
 -      case 0x03:
 -        read_key = GRUB_TERM_RIGHT;
 -        break;
 -      case 0x04:
 -        read_key = GRUB_TERM_LEFT;
 -        break;
 -      case 0x05:
 -        read_key = GRUB_TERM_HOME;
 -        break;
 -      case 0x06:
 -        read_key = GRUB_TERM_END;
 -        break;
 -      case 0x07:
 -        break;
 -      case 0x08:
 -        read_key = GRUB_TERM_DC;
 -        break;
 -      case 0x09:
 -        break;
 -      case 0x0a:
 -        break;
 -      case 0x0b:
 -        read_key = 24;
 -        break;
 -      case 0x0c:
 -        read_key = 1;
 -        break;
 -      case 0x0d:
 -        read_key = 5;
 -        break;
 -      case 0x0e:
 -        read_key = 3;
 -        break;
 -      case 0x17:
 -        read_key = '\e';
 -        break;
 -      default:
 -        break;
 -      }
 -    }
 -
 -  return read_key;
 -}
++const unsigned efi_codes[] =
++  {
++    0, GRUB_TERM_UP, GRUB_TERM_DOWN, GRUB_TERM_RIGHT,
++    GRUB_TERM_LEFT, GRUB_TERM_HOME, GRUB_TERM_END, GRUB_TERM_KEY_INSERT,
++    GRUB_TERM_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
++    GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
++    GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
++    GRUB_TERM_KEY_F10, 0, 0, '\e'
++  };
 -grub_console_getkey (struct grub_term_input *term)
+ static int
 -  grub_efi_boot_services_t *b;
 -  grub_efi_uintn_t index;
++grub_console_getkey (struct grub_term_input *term __attribute__ ((unused)))
+ {
+   grub_efi_simple_input_interface_t *i;
 -  int key;
++  grub_efi_input_key_t key;
+   grub_efi_status_t status;
 -  if (read_key >= 0)
 -    {
 -      key = read_key;
 -      read_key = -1;
 -      return key;
 -    }
 -
+   if (grub_efi_is_finished)
+     return 0;
 -  b = grub_efi_system_table->boot_services;
+   i = grub_efi_system_table->con_in;
 -  do
 -    {
 -      status = efi_call_3 (b->wait_for_event, 1, &(i->wait_for_key), &index);
 -      if (status != GRUB_EFI_SUCCESS)
 -        return -1;
++  status = efi_call_2 (i->read_key_stroke, i, &key);
 -      grub_console_checkkey (term);
 -    }
 -  while (read_key < 0);
++  if (status != GRUB_EFI_SUCCESS)
++    return GRUB_TERM_NO_KEY;
 -  key = read_key;
 -  read_key = -1;
 -  return key;
++  if (key.scan_code == 0)
++    return key.unicode_char;
++  else if (key.scan_code < ARRAY_SIZE (efi_codes))
++    return efi_codes[key.scan_code];
 -    .checkkey = grub_console_checkkey,
++  return GRUB_TERM_NO_KEY;
+ }
+ static grub_uint16_t
+ grub_console_getwh (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   grub_efi_uintn_t columns, rows;
+   o = grub_efi_system_table->con_out;
+   if (grub_efi_is_finished || efi_call_4 (o->query_mode, o, o->mode->mode,
+                                         &columns, &rows) != GRUB_EFI_SUCCESS)
+     {
+       /* Why does this fail?  */
+       columns = 80;
+       rows = 25;
+     }
+   return ((columns << 8) | rows);
+ }
+ static grub_uint16_t
+ grub_console_getxy (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   if (grub_efi_is_finished)
+     return 0;
+   o = grub_efi_system_table->con_out;
+   return ((o->mode->cursor_column << 8) | o->mode->cursor_row);
+ }
+ static void
+ grub_console_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+                    grub_uint8_t x, grub_uint8_t y)
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   if (grub_efi_is_finished)
+     return;
+   o = grub_efi_system_table->con_out;
+   efi_call_3 (o->set_cursor_position, o, x, y);
+ }
+ static void
+ grub_console_cls (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   grub_efi_int32_t orig_attr;
+   if (grub_efi_is_finished)
+     return;
+   o = grub_efi_system_table->con_out;
+   orig_attr = o->mode->attribute;
+   efi_call_2 (o->set_attributes, o, GRUB_EFI_BACKGROUND_BLACK);
+   efi_call_1 (o->clear_screen, o);
+   efi_call_2 (o->set_attributes, o, orig_attr);
+ }
+ static void
+ grub_console_setcolorstate (struct grub_term_output *term,
+                           grub_term_color_state state)
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   if (grub_efi_is_finished)
+     return;
+   o = grub_efi_system_table->con_out;
+   switch (state) {
+     case GRUB_TERM_COLOR_STANDARD:
+       efi_call_2 (o->set_attributes, o, grub_console_standard_color);
+       break;
+     case GRUB_TERM_COLOR_NORMAL:
+       efi_call_2 (o->set_attributes, o, term->normal_color);
+       break;
+     case GRUB_TERM_COLOR_HIGHLIGHT:
+       efi_call_2 (o->set_attributes, o, term->highlight_color);
+       break;
+     default:
+       break;
+   }
+ }
+ static void
+ grub_console_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+                       int on)
+ {
+   grub_efi_simple_text_output_interface_t *o;
+   if (grub_efi_is_finished)
+     return;
+   o = grub_efi_system_table->con_out;
+   efi_call_2 (o->enable_cursor, o, on);
+ }
+ static grub_err_t
+ grub_efi_console_init (struct grub_term_output *term)
+ {
+   grub_console_setcursor (term, 1);
+   return 0;
+ }
+ static grub_err_t
+ grub_efi_console_fini (struct grub_term_output *term)
+ {
+   grub_console_setcursor (term, 0);
+   return 0;
+ }
+ static struct grub_term_input grub_console_term_input =
+   {
+     .name = "console",
+     .getkey = grub_console_getkey,
+   };
+ static struct grub_term_output grub_console_term_output =
+   {
+     .name = "console",
+     .init = grub_efi_console_init,
+     .fini = grub_efi_console_fini,
+     .putchar = grub_console_putchar,
+     .getwh = grub_console_getwh,
+     .getxy = grub_console_getxy,
+     .gotoxy = grub_console_gotoxy,
+     .cls = grub_console_cls,
+     .setcolorstate = grub_console_setcolorstate,
+     .setcursor = grub_console_setcursor,
+     .normal_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_LIGHTGRAY,
+                                       GRUB_EFI_BACKGROUND_BLACK),
+     .highlight_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_BLACK,
+                                          GRUB_EFI_BACKGROUND_LIGHTGRAY),
+     .flags = GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS
+   };
+ void
+ grub_console_init (void)
+ {
+   /* FIXME: it is necessary to consider the case where no console control
+      is present but the default is already in text mode.  */
+   if (! grub_efi_set_text_mode (1))
+     {
+       grub_error (GRUB_ERR_BAD_DEVICE, "cannot set text mode");
+       return;
+     }
+   grub_term_register_input ("console", &grub_console_term_input);
+   grub_term_register_output ("console", &grub_console_term_output);
+ }
+ void
+ grub_console_fini (void)
+ {
+   grub_term_unregister_input (&grub_console_term_input);
+   grub_term_unregister_output (&grub_console_term_output);
+ }
index 0000000000000000000000000000000000000000,19a8e3814ea7c9a9785842f7bb00dca152fa3729..0efeafe4e8f35d63d151b66046628c212bf38dae
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,83 +1,68 @@@
 -#define KEYBOARD_LEFT_SHIFT   (1 << 0)
 -#define KEYBOARD_RIGHT_SHIFT  (1 << 1)
 -#define KEYBOARD_CTRL         (1 << 2)
 -#define KEYBOARD_ALT          (1 << 3)
 -
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2002,2003,2005,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/machine/memory.h>
+ #include <grub/machine/console.h>
+ #include <grub/term.h>
+ #include <grub/types.h>
+ static const struct grub_machine_bios_data_area *bios_data_area =
+   (struct grub_machine_bios_data_area *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
 -  grub_uint8_t status = bios_data_area->keyboard_flag_lower;
 -  int mods = 0;
 -
 -  if (status & (KEYBOARD_LEFT_SHIFT | KEYBOARD_RIGHT_SHIFT))
 -    mods |= GRUB_TERM_STATUS_SHIFT;
 -  if (status & KEYBOARD_CTRL)
 -    mods |= GRUB_TERM_STATUS_CTRL;
 -  if (status & KEYBOARD_ALT)
 -    mods |= GRUB_TERM_STATUS_ALT;
 -
 -  return mods;
+ static int
+ grub_console_getkeystatus (struct grub_term_input *term __attribute__ ((unused)))
+ {
 -    .checkkey = grub_console_checkkey,
++  /* conveniently GRUB keystatus is modelled after BIOS one.  */
++  return bios_data_area->keyboard_flag_lower & ~0x80;
+ }
+ static struct grub_term_input grub_console_term_input =
+   {
+     .name = "console",
 -    .getkeystatus = grub_console_getkeystatus,
+     .getkey = grub_console_getkey,
++    .getkeystatus = grub_console_getkeystatus
+   };
+ static struct grub_term_output grub_console_term_output =
+   {
+     .name = "console",
+     .putchar = grub_console_putchar,
+     .getwh = grub_console_getwh,
+     .getxy = grub_console_getxy,
+     .gotoxy = grub_console_gotoxy,
+     .cls = grub_console_cls,
+     .setcolorstate = grub_console_setcolorstate,
+     .setcursor = grub_console_setcursor,
+     .flags = GRUB_TERM_CODE_TYPE_CP437,
+     .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
+     .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
+   };
+ void
+ grub_console_init (void)
+ {
+   grub_term_register_output ("console", &grub_console_term_output);
+   grub_term_register_input ("console", &grub_console_term_input);
+ }
+ void
+ grub_console_fini (void)
+ {
+   grub_term_unregister_input (&grub_console_term_input);
+   grub_term_unregister_output (&grub_console_term_output);
+ }
index 0000000000000000000000000000000000000000,9ec85631bf6f7b4c1ea4e0296b2b45e925717f7c..3c17a2a41a2013f972dde65eccc9801986607de7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,246 +1,245 @@@
 -    .checkkey = grub_terminfo_checkkey,
+ /*  ofconsole.c -- Open Firmware console for GRUB.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2004,2005,2007,2008,2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/term.h>
+ #include <grub/types.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/time.h>
+ #include <grub/terminfo.h>
+ #include <grub/ieee1275/console.h>
+ #include <grub/ieee1275/ieee1275.h>
+ static grub_ieee1275_ihandle_t stdout_ihandle;
+ static grub_ieee1275_ihandle_t stdin_ihandle;
+ static grub_uint8_t grub_ofconsole_width;
+ static grub_uint8_t grub_ofconsole_height;
+ struct color
+ {
+   int red;
+   int green;
+   int blue;
+ };
+ /* Use serial colors as they are default on most firmwares and some firmwares
+    ignore set-color!. Additionally output may be redirected to serial.  */
+ static struct color colors[] =
+   {
+     // {R, G, B}
+     {0x00, 0x00, 0x00}, // 0 = black
+     {0xA8, 0x00, 0x00}, // 1 = red
+     {0x00, 0xA8, 0x00}, // 2 = green
+     {0xFE, 0xFE, 0x54}, // 3 = yellow
+     {0x00, 0x00, 0xA8}, // 4 = blue
+     {0xA8, 0x00, 0xA8}, // 5 = magenta
+     {0x00, 0xA8, 0xA8}, // 6 = cyan
+     {0xFE, 0xFE, 0xFE}  // 7 = white
+   };
+ static void
+ put (struct grub_term_output *term __attribute__ ((unused)), const int c)
+ {
+   char chr = c;
+   grub_ieee1275_write (stdout_ihandle, &chr, 1, 0);
+ }
+ static int
+ readkey (struct grub_term_input *term __attribute__ ((unused)))
+ {
+   grub_uint8_t c;
+   grub_ssize_t actual = 0;
+   grub_ieee1275_read (stdin_ihandle, &c, 1, &actual);
+   if (actual > 0)
+     return c;
+   return -1;
+ }
+ static void
+ grub_ofconsole_dimensions (void)
+ {
+   grub_ieee1275_ihandle_t options;
+   grub_ssize_t lval;
+   if (! grub_ieee1275_finddevice ("/options", &options)
+       && options != (grub_ieee1275_ihandle_t) -1)
+     {
+       if (! grub_ieee1275_get_property_length (options, "screen-#columns",
+                                              &lval)
+         && lval >= 0 && lval < 1024)
+       {
+         char val[lval];
+         if (! grub_ieee1275_get_property (options, "screen-#columns",
+                                           val, lval, 0))
+           grub_ofconsole_width = (grub_uint8_t) grub_strtoul (val, 0, 10);
+       }
+       if (! grub_ieee1275_get_property_length (options, "screen-#rows", &lval)
+         && lval >= 0 && lval < 1024)
+       {
+         char val[lval];
+         if (! grub_ieee1275_get_property (options, "screen-#rows",
+                                           val, lval, 0))
+           grub_ofconsole_height = (grub_uint8_t) grub_strtoul (val, 0, 10);
+       }
+     }
+   /* Use a small console by default.  */
+   if (! grub_ofconsole_width)
+     grub_ofconsole_width = 80;
+   if (! grub_ofconsole_height)
+     grub_ofconsole_height = 24;
+ }
+ static grub_uint16_t
+ grub_ofconsole_getwh (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   return (grub_ofconsole_width << 8) | grub_ofconsole_height;
+ }
+ static void
+ grub_ofconsole_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+                         int on)
+ {
+   /* Understood by the Open Firmware flavour in OLPC.  */
+   if (on)
+     grub_ieee1275_interpret ("cursor-on", 0);
+   else
+     grub_ieee1275_interpret ("cursor-off", 0);
+ }
+ static grub_err_t
+ grub_ofconsole_init_input (struct grub_term_input *term)
+ {
+   grub_ssize_t actual;
+   if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdin", &stdin_ihandle,
+                                         sizeof stdin_ihandle, &actual)
+       || actual != sizeof stdin_ihandle)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdin");
+   return grub_terminfo_input_init (term);
+ }
+ static grub_err_t
+ grub_ofconsole_init_output (struct grub_term_output *term)
+ {
+   grub_ssize_t actual;
+   /* The latest PowerMacs don't actually initialize the screen for us, so we
+    * use this trick to re-open the output device (but we avoid doing this on
+    * platforms where it's known to be broken). */
+   if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT))
+     grub_ieee1275_interpret ("output-device output", 0);
+   if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "stdout", &stdout_ihandle,
+                                         sizeof stdout_ihandle, &actual)
+       || actual != sizeof stdout_ihandle)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot find stdout");
+   /* Initialize colors.  */
+   if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS))
+     {
+       unsigned col;
+       for (col = 0; col < ARRAY_SIZE (colors); col++)
+       grub_ieee1275_set_color (stdout_ihandle, col, colors[col].red,
+                                colors[col].green, colors[col].blue);
+       /* Set the right fg and bg colors.  */
+       grub_terminfo_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+     }
+   grub_ofconsole_dimensions ();
+   return 0;
+ }
\f
+ struct grub_terminfo_input_state grub_ofconsole_terminfo_input =
+   {
+     .readkey = readkey
+   };
+ struct grub_terminfo_output_state grub_ofconsole_terminfo_output =
+   {
+     .put = put
+   };
+ static struct grub_term_input grub_ofconsole_term_input =
+   {
+     .name = "ofconsole",
+     .init = grub_ofconsole_init_input,
+     .getkey = grub_terminfo_getkey,
+     .data = &grub_ofconsole_terminfo_input
+   };
+ static struct grub_term_output grub_ofconsole_term_output =
+   {
+     .name = "ofconsole",
+     .init = grub_ofconsole_init_output,
+     .putchar = grub_terminfo_putchar,
+     .getxy = grub_terminfo_getxy,
+     .getwh = grub_ofconsole_getwh,
+     .gotoxy = grub_terminfo_gotoxy,
+     .cls = grub_terminfo_cls,
+     .setcolorstate = grub_terminfo_setcolorstate,
+     .setcursor = grub_ofconsole_setcursor,
+     .flags = GRUB_TERM_CODE_TYPE_ASCII,
+     .data = &grub_ofconsole_terminfo_output,
+     .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
+     .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
+   };
+ void grub_terminfo_fini (void);
+ void grub_terminfo_init (void);
+ void
+ grub_console_init_early (void)
+ {
+   grub_term_register_input ("ofconsole", &grub_ofconsole_term_input);
+   grub_term_register_output ("ofconsole", &grub_ofconsole_term_output);
+ }
+ void
+ grub_console_init_lately (void)
+ {
+   const char *type;
+   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI))
+     type = "dumb";
+   else
+     type = "ieee1275";
+   grub_terminfo_init ();
+   grub_terminfo_output_register (&grub_ofconsole_term_output, type);
+ }
+ void
+ grub_console_fini (void)
+ {
+   grub_term_unregister_input (&grub_ofconsole_term_input);
+   grub_term_unregister_output (&grub_ofconsole_term_output);
+   grub_terminfo_output_unregister (&grub_ofconsole_term_output);
+   grub_terminfo_fini ();
+ }
index 0000000000000000000000000000000000000000,2268788af25143d4210e224b3152672f5ff31af5..f435a7bc5a1670de61d679f1f795046f6f09b2c7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,363 +1,362 @@@
 -  .checkkey = grub_terminfo_checkkey,
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/serial.h>
+ #include <grub/term.h>
+ #include <grub/types.h>
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+ #include <grub/terminfo.h>
+ #include <grub/cpu/io.h>
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ #include <grub/list.h>
+ #define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
+ /* Argument options.  */
+ static const struct grub_arg_option options[] =
+ {
+   {"unit",   'u', 0, N_("Set the serial unit."),             0, ARG_TYPE_INT},
+   {"port",   'p', 0, N_("Set the serial port address."),     0, ARG_TYPE_STRING},
+   {"speed",  's', 0, N_("Set the serial port speed."),       0, ARG_TYPE_INT},
+   {"word",   'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT},
+   {"parity", 'r', 0, N_("Set the serial port parity."),      0, ARG_TYPE_STRING},
+   {"stop",   't', 0, N_("Set the serial port stop bits."),   0, ARG_TYPE_INT},
+   {0, 0, 0, 0, 0, 0}
+ };
+ struct grub_serial_port *grub_serial_ports;
+ struct grub_serial_output_state
+ {
+   struct grub_terminfo_output_state tinfo;
+   struct grub_serial_port *port;
+ };
+ struct grub_serial_input_state
+ {
+   struct grub_terminfo_input_state tinfo;
+   struct grub_serial_port *port;
+ };
+ static grub_uint16_t
+ grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
+ {
+   const grub_uint8_t TEXT_WIDTH = 80;
+   const grub_uint8_t TEXT_HEIGHT = 24;
+   return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
+ }
+ static void 
+ serial_put (grub_term_output_t term, const int c)
+ {
+   struct grub_serial_output_state *data = term->data;
+   data->port->driver->put (data->port, c);
+ }
+ static int
+ serial_fetch (grub_term_input_t term)
+ {
+   struct grub_serial_input_state *data = term->data;
+   return data->port->driver->fetch (data->port);
+ }
+ struct grub_serial_input_state grub_serial_terminfo_input =
+   {
+     .tinfo =
+     {
+       .readkey = serial_fetch
+     }
+   };
+ struct grub_serial_output_state grub_serial_terminfo_output =
+   {
+     .tinfo =
+     {
+       .put = serial_put
+     }
+   };
+ int registered = 0;
+ static struct grub_term_input grub_serial_term_input =
+ {
+   .name = "serial",
+   .init = grub_terminfo_input_init,
+   .getkey = grub_terminfo_getkey,
+   .data = &grub_serial_terminfo_input
+ };
+ static struct grub_term_output grub_serial_term_output =
+ {
+   .name = "serial",
+   .putchar = grub_terminfo_putchar,
+   .getwh = grub_serial_getwh,
+   .getxy = grub_terminfo_getxy,
+   .gotoxy = grub_terminfo_gotoxy,
+   .cls = grub_terminfo_cls,
+   .setcolorstate = grub_terminfo_setcolorstate,
+   .setcursor = grub_terminfo_setcursor,
+   .flags = GRUB_TERM_CODE_TYPE_ASCII,
+   .data = &grub_serial_terminfo_output,
+   .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
+   .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
+ };
\f
+ static struct grub_serial_port *
+ grub_serial_find (char *name)
+ {
+   struct grub_serial_port *port;
+   FOR_SERIAL_PORTS (port)
+     if (grub_strcmp (port->name, name) == 0)
+       break;
+ #ifndef GRUB_MACHINE_EMU
+   if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
+       && grub_isdigit (name [sizeof ("port") - 1]))
+     {
+       name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
+                                                       0, 16));
+       if (!name)
+       return NULL;
+       FOR_SERIAL_PORTS (port)
+       if (grub_strcmp (port->name, name) == 0)
+         break;
+     }
+ #endif
+   return port;
+ }
+ static grub_err_t
+ grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args)
+ {
+   struct grub_arg_list *state = cmd->state;
+   char pname[40];
+   char *name = NULL;
+   struct grub_serial_port *port;
+   struct grub_serial_config config;
+   grub_err_t err;
+   if (state[0].set)
+     {
+       grub_snprintf (pname, sizeof (pname), "com%ld",
+                    grub_strtoul (state[0].arg, 0, 0));
+       name = pname;
+     }
+   if (state[1].set)
+     {
+       grub_snprintf (pname, sizeof (pname), "port%lx",
+                    grub_strtoul (state[1].arg, 0, 0));
+       name = pname;
+     }
+   if (argc >= 1)
+     name = args[0];
+   if (!name)
+     name = "com0";
+   port = grub_serial_find (name);
+   if (!port)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown serial port");
+   config = port->config;
+   if (state[2].set)
+     config.speed = grub_strtoul (state[2].arg, 0, 0);
+   if (state[3].set)
+     config.word_len = grub_strtoul (state[3].arg, 0, 0);
+   if (state[4].set)
+     {
+       if (! grub_strcmp (state[4].arg, "no"))
+       config.parity = GRUB_SERIAL_PARITY_NONE;
+       else if (! grub_strcmp (state[4].arg, "odd"))
+       config.parity = GRUB_SERIAL_PARITY_ODD;
+       else if (! grub_strcmp (state[4].arg, "even"))
+       config.parity = GRUB_SERIAL_PARITY_EVEN;
+       else
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
+     }
+   if (state[5].set)
+     {
+       if (! grub_strcmp (state[5].arg, "1"))
+       config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
+       else if (! grub_strcmp (state[5].arg, "2"))
+       config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
+       else
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
+     }
+   /* Initialize with new settings.  */
+   err = port->driver->configure (port, &config);
+   if (err)
+     return err;
+ #ifndef GRUB_MACHINE_EMU
+   /* Compatibility kludge.  */
+   if (port->driver == &grub_ns8250_driver)
+     {
+       if (!registered)
+       {
+         grub_term_register_input ("serial", &grub_serial_term_input);
+         grub_term_register_output ("serial", &grub_serial_term_output);
+       }
+       grub_serial_terminfo_output.port = port;
+       grub_serial_terminfo_input.port = port;
+       registered = 1;
+     }
+ #endif
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_serial_register (struct grub_serial_port *port)
+ {
+   struct grub_term_input *in;
+   struct grub_term_output *out;
+   struct grub_serial_input_state *indata;
+   struct grub_serial_output_state *outdata;
+   in = grub_malloc (sizeof (*in));
+   if (!in)
+     return grub_errno;
+   indata = grub_malloc (sizeof (*indata));
+   if (!indata)
+     {
+       grub_free (in);
+       return grub_errno;
+     }
+   grub_memcpy (in, &grub_serial_term_input, sizeof (*in));
+   in->data = indata;
+   in->name = grub_xasprintf ("serial_%s", port->name);
+   grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata));
+   if (!in->name)
+     {
+       grub_free (in);
+       grub_free (indata);
+       return grub_errno;
+     }
+   
+   out = grub_malloc (sizeof (*out));
+   if (!out)
+     {
+       grub_free (in);
+       grub_free (indata);
+       grub_free ((char *) in->name);
+       return grub_errno;
+     }
+   outdata = grub_malloc (sizeof (*outdata));
+   if (!outdata)
+     {
+       grub_free (in);
+       grub_free (indata);
+       grub_free ((char *) in->name);
+       grub_free (out);
+       return grub_errno;
+     }
+   grub_memcpy (out, &grub_serial_term_output, sizeof (*out));
+   out->data = outdata;
+   out->name = in->name;
+   grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata));
+   grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
+   ((struct grub_serial_input_state *) in->data)->port = port;
+   ((struct grub_serial_output_state *) out->data)->port = port;
+   port->term_in = in;
+   port->term_out = out;
+   grub_terminfo_output_register (out, "vt100");
+ #ifdef GRUB_MACHINE_MIPS_YEELOONG
+   if (grub_strcmp (port->name, "com0") == 0)
+     {
+       grub_term_register_input_active ("serial_*", in);
+       grub_term_register_output_active ("serial_*", out);
+     }
+   else
+ #endif
+     {
+       grub_term_register_input ("serial_*", in);
+       grub_term_register_output ("serial_*", out);
+     }
+   return GRUB_ERR_NONE;
+ }
+ void
+ grub_serial_unregister (struct grub_serial_port *port)
+ {
+   if (port->driver->fini)
+     port->driver->fini (port);
+   
+   if (port->term_in)
+     grub_term_unregister_input (port->term_in);
+   if (port->term_out)
+     grub_term_unregister_output (port->term_out);
+   grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
+ }
+ void
+ grub_serial_unregister_driver (struct grub_serial_driver *driver)
+ {
+   struct grub_serial_port *port, *next;
+   for (port = grub_serial_ports; port; port = next)
+     {
+       next = port->next;
+       if (port->driver == driver)
+       grub_serial_unregister (port);
+     }
+ }
+ static grub_extcmd_t cmd;
+ GRUB_MOD_INIT(serial)
+ {
+   cmd = grub_register_extcmd ("serial", grub_cmd_serial,
+                             GRUB_COMMAND_FLAG_BOTH,
+                             N_("[OPTIONS...]"),
+                             N_("Configure serial port."), options);
+ #ifndef GRUB_MACHINE_EMU
+   grub_ns8250_init ();
+ #endif
+ }
+ GRUB_MOD_FINI(serial)
+ {
+   while (grub_serial_ports)
+     grub_serial_unregister (grub_serial_ports);
+   if (registered)
+     {
+       grub_term_unregister_input (&grub_serial_term_input);
+       grub_term_unregister_output (&grub_serial_term_output);
+     }
+   grub_unregister_extcmd (cmd);
+ }
index 0000000000000000000000000000000000000000,5691b9cc681291abc84fcd7f4388a64ff5d624ca..8dea7aea1565a3873b6898bff078f0bb3ab5a0dc
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,630 +1,621 @@@
 -      char ascii;
+ /* terminfo.c - simple terminfo module */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2003,2004,2005,2007  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ /*
+  * This file contains various functions dealing with different
+  * terminal capabilities. For example, vt52 and vt100.
+  */
+ #include <grub/types.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/err.h>
+ #include <grub/dl.h>
+ #include <grub/term.h>
+ #include <grub/terminfo.h>
+ #include <grub/tparm.h>
+ #include <grub/command.h>
+ #include <grub/i18n.h>
+ #include <grub/time.h>
+ static struct grub_term_output *terminfo_outputs;
+ /* Get current terminfo name.  */
+ char *
+ grub_terminfo_get_current (struct grub_term_output *term)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   return data->name;
+ }
+ /* Free *PTR and set *PTR to NULL, to prevent double-free.  */
+ static void
+ grub_terminfo_free (char **ptr)
+ {
+   grub_free (*ptr);
+   *ptr = 0;
+ }
+ static void
+ grub_terminfo_all_free (struct grub_term_output *term)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   /* Free previously allocated memory.  */
+   grub_terminfo_free (&data->name);
+   grub_terminfo_free (&data->gotoxy);
+   grub_terminfo_free (&data->cls);
+   grub_terminfo_free (&data->reverse_video_on);
+   grub_terminfo_free (&data->reverse_video_off);
+   grub_terminfo_free (&data->cursor_on);
+   grub_terminfo_free (&data->cursor_off);
+ }
+ /* Set current terminfo type.  */
+ grub_err_t
+ grub_terminfo_set_current (struct grub_term_output *term,
+                          const char *str)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   /* TODO
+    * Lookup user specified terminfo type. If found, set term variables
+    * as appropriate. Otherwise return an error.
+    *
+    * How should this be done?
+    *  a. A static table included in this module.
+    *     - I do not like this idea.
+    *  b. A table stored in the configuration directory.
+    *     - Users must convert their terminfo settings if we have not already.
+    *  c. Look for terminfo files in the configuration directory.
+    *     - /usr/share/terminfo is 6.3M on my system.
+    *     - /usr/share/terminfo is not on most users boot partition.
+    *     + Copying the terminfo files you want to use to the grub
+    *       configuration directory is easier then (b).
+    *  d. Your idea here.
+    */
+   grub_terminfo_all_free (term);
+   if (grub_strcmp ("vt100", str) == 0)
+     {
+       data->name              = grub_strdup ("vt100");
+       data->gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
+       data->cls               = grub_strdup ("\e[H\e[J");
+       data->reverse_video_on  = grub_strdup ("\e[7m");
+       data->reverse_video_off = grub_strdup ("\e[m");
+       data->cursor_on         = grub_strdup ("\e[?25h");
+       data->cursor_off        = grub_strdup ("\e[?25l");
+       data->setcolor          = NULL;
+       return grub_errno;
+     }
+   if (grub_strcmp ("vt100-color", str) == 0)
+     {
+       data->name              = grub_strdup ("vt100-color");
+       data->gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
+       data->cls               = grub_strdup ("\e[H\e[J");
+       data->reverse_video_on  = grub_strdup ("\e[7m");
+       data->reverse_video_off = grub_strdup ("\e[m");
+       data->cursor_on         = grub_strdup ("\e[?25h");
+       data->cursor_off        = grub_strdup ("\e[?25l");
+       data->setcolor          = grub_strdup ("\e[3%p1%dm\e[4%p2%dm");
+       return grub_errno;
+     }
+   if (grub_strcmp ("ieee1275", str) == 0)
+     {
+       data->name              = grub_strdup ("ieee1275");
+       data->gotoxy            = grub_strdup ("\e[%i%p1%d;%p2%dH");
+       /* Clear the screen.  Using serial console, screen(1) only recognizes the
+        * ANSI escape sequence.  Using video console, Apple Open Firmware
+        * (version 3.1.1) only recognizes the literal ^L.  So use both.  */
+       data->cls               = grub_strdup ("\f\e[2J");
+       data->reverse_video_on  = grub_strdup ("\e[7m");
+       data->reverse_video_off = grub_strdup ("\e[m");
+       data->cursor_on         = grub_strdup ("\e[?25h");
+       data->cursor_off        = grub_strdup ("\e[?25l");
+       data->setcolor          = grub_strdup ("\e[3%p1%dm\e[4%p2%dm");
+       return grub_errno;
+     }
+   if (grub_strcmp ("dumb", str) == 0)
+     {
+       data->name              = grub_strdup ("dumb");
+       data->gotoxy            = NULL;
+       data->cls               = NULL;
+       data->reverse_video_on  = NULL;
+       data->reverse_video_off = NULL;
+       data->cursor_on         = NULL;
+       data->cursor_off        = NULL;
+       data->setcolor          = NULL;
+       return grub_errno;
+     }
+   return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type");
+ }
+ grub_err_t
+ grub_terminfo_output_register (struct grub_term_output *term,
+                              const char *type)
+ {
+   grub_err_t err;
+   struct grub_terminfo_output_state *data;
+   err = grub_terminfo_set_current (term, type);
+   if (err)
+     return err;
+   data = (struct grub_terminfo_output_state *) term->data;
+   data->next = terminfo_outputs;
+   terminfo_outputs = term;
+   return GRUB_ERR_NONE;
+ }
+ grub_err_t
+ grub_terminfo_output_unregister (struct grub_term_output *term)
+ {
+   struct grub_term_output **ptr;
+   for (ptr = &terminfo_outputs; *ptr;
+        ptr = &((struct grub_terminfo_output_state *) (*ptr)->data)->next)
+     if (*ptr == term)
+       {
+       grub_terminfo_all_free (term);
+       *ptr = ((struct grub_terminfo_output_state *) (*ptr)->data)->next;
+       return GRUB_ERR_NONE;
+       }
+   return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminal not found");
+ }
+ /* Wrapper for grub_putchar to write strings.  */
+ static void
+ putstr (struct grub_term_output *term, const char *str)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   while (*str)
+     data->put (term, *str++);
+ }
+ grub_uint16_t
+ grub_terminfo_getxy (struct grub_term_output *term)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   return ((data->xpos << 8) | data->ypos);
+ }
+ void
+ grub_terminfo_gotoxy (struct grub_term_output *term,
+                     grub_uint8_t x, grub_uint8_t y)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   if (x > grub_term_width (term) || y > grub_term_height (term))
+     {
+       grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", x, y);
+       return;
+     }
+   if (data->gotoxy)
+     putstr (term, grub_terminfo_tparm (data->gotoxy, y, x));
+   else
+     {
+       if ((y == data->ypos) && (x == data->xpos - 1))
+       data->put (term, '\b');
+     }
+   data->xpos = x;
+   data->ypos = y;
+ }
+ /* Clear the screen.  */
+ void
+ grub_terminfo_cls (struct grub_term_output *term)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   putstr (term, grub_terminfo_tparm (data->cls));
+   data->xpos = data->ypos = 0;
+ }
+ void
+ grub_terminfo_setcolorstate (struct grub_term_output *term,
+                            const grub_term_color_state state)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   if (data->setcolor)
+     {
+       int fg;
+       int bg;
+       /* Map from VGA to terminal colors.  */
+       const int colormap[8] 
+       = { 0, /* Black. */
+           4, /* Blue. */
+           2, /* Green. */
+           6, /* Cyan. */
+           1, /* Red.  */
+           5, /* Magenta.  */
+           3, /* Yellow.  */
+           7, /* White.  */
+       };
+       switch (state)
+       {
+       case GRUB_TERM_COLOR_STANDARD:
+       case GRUB_TERM_COLOR_NORMAL:
+         fg = term->normal_color & 0x0f;
+         bg = term->normal_color >> 4;
+         break;
+       case GRUB_TERM_COLOR_HIGHLIGHT:
+         fg = term->highlight_color & 0x0f;
+         bg = term->highlight_color >> 4;
+         break;
+       default:
+         return;
+       }
+       putstr (term, grub_terminfo_tparm (data->setcolor, colormap[fg & 7],
+                                        colormap[bg & 7]));
+       return;
+     }
+   switch (state)
+     {
+     case GRUB_TERM_COLOR_STANDARD:
+     case GRUB_TERM_COLOR_NORMAL:
+       putstr (term, grub_terminfo_tparm (data->reverse_video_off));
+       break;
+     case GRUB_TERM_COLOR_HIGHLIGHT:
+       putstr (term, grub_terminfo_tparm (data->reverse_video_on));
+       break;
+     default:
+       break;
+     }
+ }
+ void
+ grub_terminfo_setcursor (struct grub_term_output *term, const int on)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   if (on)
+     putstr (term, grub_terminfo_tparm (data->cursor_on));
+   else
+     putstr (term, grub_terminfo_tparm (data->cursor_off));
+ }
+ /* The terminfo version of putchar.  */
+ void
+ grub_terminfo_putchar (struct grub_term_output *term,
+                      const struct grub_unicode_glyph *c)
+ {
+   struct grub_terminfo_output_state *data
+     = (struct grub_terminfo_output_state *) term->data;
+   /* Keep track of the cursor.  */
+   switch (c->base)
+     {
+     case '\a':
+       break;
+     case '\b':
+     case 127:
+       if (data->xpos > 0)
+       data->xpos--;
+     break;
+     case '\n':
+       if (data->ypos < grub_term_height (term) - 1)
+       data->ypos++;
+       break;
+     case '\r':
+       data->xpos = 0;
+       break;
+     default:
+       if (data->xpos + c->estimated_width >= grub_term_width (term) + 1)
+       {
+         data->xpos = 0;
+         if (data->ypos < grub_term_height (term) - 1)
+           data->ypos++;
+         data->put (term, '\r');
+         data->put (term, '\n');
+       }
+       data->xpos += c->estimated_width;
+       break;
+     }
+   data->put (term, c->base);
+ }
+ #define ANSI_C0 0x9b
+ static void
+ grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
+                      int (*readkey) (struct grub_term_input *term))
+ {
+   int c;
+ #define CONTINUE_READ                                         \
+   {                                                           \
+     grub_uint64_t start;                                      \
+     /* On 9600 we have to wait up to 12 milliseconds.  */     \
+     start = grub_get_time_ms ();                              \
+     do                                                                \
+       c = readkey (term);                                     \
+     while (c == -1 && grub_get_time_ms () - start < 12);      \
+     if (c == -1)                                              \
+       return;                                                 \
+                                                               \
+     keys[*len] = c;                                           \
+     (*len)++;                                                 \
+   }
+   c = readkey (term);
+   if (c < 0)
+     {
+       *len = 0;
+       return;
+     }
+   *len = 1;
+   keys[0] = c;
+   if (c != ANSI_C0 && c != '\e')
+     {
+       /* Backspace: Ctrl-h.  */
+       if (c == 0x7f)
+       c = '\b'; 
+       *len = 1;
+       keys[0] = c;
+       return;
+     }
+   {
+     static struct
+     {
+       char key;
 -      {'4', GRUB_TERM_DC},
 -      {'A', GRUB_TERM_UP},
 -      {'B', GRUB_TERM_DOWN},
 -      {'C', GRUB_TERM_RIGHT},
 -      {'D', GRUB_TERM_LEFT},
 -      {'F', GRUB_TERM_END},
 -      {'H', GRUB_TERM_HOME},
 -      {'K', GRUB_TERM_END},
 -      {'P', GRUB_TERM_DC},
 -      {'?', GRUB_TERM_PPAGE},
 -      {'/', GRUB_TERM_NPAGE}
++      unsigned ascii;
+     }
+     three_code_table[] =
+       {
 -      char ascii;
++      {'4', GRUB_TERM_KEY_DC},
++      {'A', GRUB_TERM_KEY_UP},
++      {'B', GRUB_TERM_KEY_DOWN},
++      {'C', GRUB_TERM_KEY_RIGHT},
++      {'D', GRUB_TERM_KEY_LEFT},
++      {'F', GRUB_TERM_KEY_END},
++      {'H', GRUB_TERM_KEY_HOME},
++      {'K', GRUB_TERM_KEY_END},
++      {'P', GRUB_TERM_KEY_DC},
++      {'?', GRUB_TERM_KEY_PPAGE},
++      {'/', GRUB_TERM_KEY_NPAGE}
+       };
+     static struct
+     {
+       char key;
 -      {'1', GRUB_TERM_HOME},
 -      {'3', GRUB_TERM_DC},
 -      {'5', GRUB_TERM_PPAGE},
 -      {'6', GRUB_TERM_NPAGE}
++      unsigned ascii;
+     }
+     four_code_table[] =
+       {
 -/* The terminfo version of checkkey.  */
++      {'1', GRUB_TERM_KEY_HOME},
++      {'3', GRUB_TERM_KEY_DC},
++      {'5', GRUB_TERM_KEY_PPAGE},
++      {'6', GRUB_TERM_KEY_NPAGE}
+       };
+     unsigned i;
+     if (c == '\e')
+       {
+       CONTINUE_READ;
+       if (c != '[')
+         return;
+       }
+     CONTINUE_READ;
+       
+     for (i = 0; i < ARRAY_SIZE (three_code_table); i++)
+       if (three_code_table[i].key == c)
+       {
+         keys[0] = three_code_table[i].ascii;
+         *len = 1;
+         return;
+       }
+     for (i = 0; i < ARRAY_SIZE (four_code_table); i++)
+       if (four_code_table[i].key == c)
+       {
+         CONTINUE_READ;
+         if (c != '~')
+           return;
+         keys[0] = three_code_table[i].ascii;
+         *len = 1;
+         return;
+       }
+     return;
+   }
+ #undef CONTINUE_READ
+ }
 -grub_terminfo_checkkey (struct grub_term_input *termi)
++/* The terminfo version of getkey.  */
+ int
 -    return data->input_buf[0];
++grub_terminfo_getkey (struct grub_term_input *termi)
+ {
+   struct grub_terminfo_input_state *data
+     = (struct grub_terminfo_input_state *) (termi->data);
+   if (data->npending)
 -    return data->input_buf[0];
 -
 -  return -1;
 -}
++    {
++      data->npending--;
++      grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
++      return data->input_buf[0];
++    }
+   grub_terminfo_readkey (termi, data->input_buf,
+                        &data->npending, data->readkey);
+   if (data->npending)
 -/* The terminfo version of getkey.  */
 -int
 -grub_terminfo_getkey (struct grub_term_input *termi)
 -{
 -  struct grub_terminfo_input_state *data
 -    = (struct grub_terminfo_input_state *) (termi->data);
 -  int ret;
 -  while (! data->npending)
 -    grub_terminfo_readkey (termi, data->input_buf, &data->npending,
 -                         data->readkey);
 -
 -  ret = data->input_buf[0];
 -  data->npending--;
 -  grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
 -  return ret;
++    {
++      data->npending--;
++      grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
++      return data->input_buf[0];
++    }
++  return GRUB_TERM_NO_KEY;
+ }
+ grub_err_t
+ grub_terminfo_input_init (struct grub_term_input *termi)
+ {
+   struct grub_terminfo_input_state *data
+     = (struct grub_terminfo_input_state *) (termi->data);
+   data->npending = 0;
+   return GRUB_ERR_NONE;
+ }
+ /* GRUB Command.  */
+ static grub_err_t
+ print_terminfo (void)
+ {
+   const char *encoding_names[(GRUB_TERM_CODE_TYPE_MASK 
+                             >> GRUB_TERM_CODE_TYPE_SHIFT) + 1]
+     = {
+     /* VGA and glyph descriptor types are just for completeness,
+        they are not used on terminfo terminals.
+     */
+     [GRUB_TERM_CODE_TYPE_ASCII >> GRUB_TERM_CODE_TYPE_SHIFT] = _("ASCII"),
+     [GRUB_TERM_CODE_TYPE_CP437 >> GRUB_TERM_CODE_TYPE_SHIFT] = "CP-437",
+     [GRUB_TERM_CODE_TYPE_UTF8_LOGICAL >> GRUB_TERM_CODE_TYPE_SHIFT]
+     = _("UTF-8"),
+     [GRUB_TERM_CODE_TYPE_UTF8_VISUAL >> GRUB_TERM_CODE_TYPE_SHIFT]
+     = _("UTF-8 visual"),
+     [GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS >> GRUB_TERM_CODE_TYPE_SHIFT]
+     = "Glyph descriptors",
+     _("Unknown"), _("Unknown"), _("Unknown")
+   };
+   struct grub_term_output *cur;
+   grub_printf ("Current terminfo types: \n");
+   for (cur = terminfo_outputs; cur;
+        cur = ((struct grub_terminfo_output_state *) cur->data)->next)
+     grub_printf ("%s: %s\t%s\n", cur->name,
+                grub_terminfo_get_current(cur),
+                encoding_names[(cur->flags & GRUB_TERM_CODE_TYPE_MASK)
+                               >> GRUB_TERM_CODE_TYPE_SHIFT]);
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_cmd_terminfo (grub_command_t cmd __attribute__ ((unused)),
+                  int argc, char **args)
+ {
+   struct grub_term_output *cur;
+   int encoding = GRUB_TERM_CODE_TYPE_ASCII;
+   int i;
+   char *name = NULL, *type = NULL;
+   if (argc == 0)
+     return print_terminfo ();
+   for (i = 0; i < argc; i++)
+     {
+       if (grub_strcmp (args[i], "-a") == 0
+         || grub_strcmp (args[i], "--ascii") == 0)
+       {
+         encoding = GRUB_TERM_CODE_TYPE_ASCII;
+         continue;
+       }
+       if (grub_strcmp (args[i], "-u") == 0
+         || grub_strcmp (args[i], "--utf8") == 0)
+       {
+         encoding = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL;
+         continue;
+       }
+       if (grub_strcmp (args[i], "-v") == 0
+         || grub_strcmp (args[i], "--visual-utf8") == 0)
+       {
+         encoding = GRUB_TERM_CODE_TYPE_UTF8_VISUAL;
+         continue;
+       }
+       if (!name)
+       {
+         name = args[i];
+         continue;
+       }
+       if (!type)
+       {
+         type = args[i];
+         continue;
+       }
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters");
+     }
+   if (name == NULL)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "too few parameters");
+   for (cur = terminfo_outputs; cur;
+        cur = ((struct grub_terminfo_output_state *) cur->data)->next)
+     if (grub_strcmp (name, cur->name) == 0)
+       {
+       cur->flags = (cur->flags & ~GRUB_TERM_CODE_TYPE_MASK) | encoding;
+       if (!type)
+         return GRUB_ERR_NONE;
+       return grub_terminfo_set_current (cur, type);
+       }
+   return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                    "no terminal %s found or it's not handled by terminfo",
+                    name);
+ }
+ static grub_command_t cmd;
+ GRUB_MOD_INIT(terminfo)
+ {
+   cmd = grub_register_command ("terminfo", grub_cmd_terminfo,
+                              N_("[[-a|-u|-v] TERM [TYPE]]"),
+                              N_("Set terminfo type of TERM  to TYPE.\n"
+                                 "-a, --ascii            Terminal is ASCII-only [default].\n"
+                                 "-u, --utf8             Terminal is logical-ordered UTF-8.\n"
+                                 "-v, --visual-utf8      Terminal is visually-ordered UTF-8.")
+                              );
+ }
+ GRUB_MOD_FINI(terminfo)
+ {
+   grub_unregister_command (cmd);
+ }
index 0000000000000000000000000000000000000000,d875ac00a5ebd0a05029ba5b7a959722868a0e2e..6b1485e96d0430f507aaa05377f05abbc9506f40
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,316 +1,395 @@@
 -static char keyboard_map[128] =
+ /* Support for the HID Boot Protocol.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2008, 2009  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 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB 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, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/term.h>
+ #include <grub/time.h>
+ #include <grub/cpu/io.h>
+ #include <grub/misc.h>
+ #include <grub/term.h>
+ #include <grub/usb.h>
+ #include <grub/dl.h>
+ #include <grub/time.h>
++#include <grub/keyboard_layouts.h>
\f
 -    '\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
 -    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
 -    'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
 -    'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
 -    '3', '4', '5', '6', '7', '8', '9', '0',
 -    '\n', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[',
 -    ']', '\\', '#', ';', '\'', '`', ',', '.',
 -    '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
 -    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
 -    '\0', '\0', GRUB_TERM_HOME, GRUB_TERM_PPAGE, GRUB_TERM_DC, GRUB_TERM_END, GRUB_TERM_NPAGE, GRUB_TERM_RIGHT,
 -    GRUB_TERM_LEFT, GRUB_TERM_DOWN, GRUB_TERM_UP
++
++enum
+   {
 -static char keyboard_map_shift[128] =
++    KEY_NO_KEY = 0x00,
++    KEY_ERR_BUFFER = 0x01,
++    KEY_ERR_POST  = 0x02,
++    KEY_ERR_UNDEF = 0x03,
++    KEY_CAPS_LOCK = 0x39,
++    KEY_NUM_LOCK  = 0x53,
+   };
 -    '\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
 -    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
 -    'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
 -    'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
 -    '#', '$', '%', '^', '&', '*', '(', ')',
 -    '\n', '\0', '\0', '\0', ' ', '_', '+', '{',
 -    '}', '|', '#', ':', '"', '`', '<', '>',
 -    '?'
++enum
+   {
 -
++    LED_NUM_LOCK = 0x01,
++    LED_CAPS_LOCK = 0x02
+   };
 -static int grub_usb_keyboard_checkkey (struct grub_term_input *term);
+ /* Valid values for bRequest.  See HID definition version 1.11 section 7.2. */
+ #define USB_HID_GET_REPORT    0x01
+ #define USB_HID_GET_IDLE      0x02
+ #define USB_HID_GET_PROTOCOL  0x03
+ #define USB_HID_SET_REPORT    0x09
+ #define USB_HID_SET_IDLE      0x0A
+ #define USB_HID_SET_PROTOCOL  0x0B
+ #define USB_HID_BOOT_SUBCLASS 0x01
+ #define USB_HID_KBD_PROTOCOL  0x01
 -    .checkkey = grub_usb_keyboard_checkkey,
++#define GRUB_USB_KEYBOARD_LEFT_CTRL   0x01
++#define GRUB_USB_KEYBOARD_LEFT_SHIFT  0x02
++#define GRUB_USB_KEYBOARD_LEFT_ALT    0x04
++#define GRUB_USB_KEYBOARD_RIGHT_CTRL  0x10
++#define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
++#define GRUB_USB_KEYBOARD_RIGHT_ALT   0x40
++
++struct grub_usb_keyboard_data
++{
++  grub_usb_device_t usbdev;
++  grub_uint8_t status;
++  grub_uint16_t mods;
++  int interfno;
++  struct grub_usb_desc_endp *endp;
++  grub_usb_transfer_t transfer;
++  grub_uint8_t report[8];
++  int dead;
++  int last_key;
++  grub_uint64_t repeat_time;
++};
++
+ static int grub_usb_keyboard_getkey (struct grub_term_input *term);
+ static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
+ static struct grub_term_input grub_usb_keyboard_term =
+   {
 -struct grub_usb_keyboard_data
+     .getkey = grub_usb_keyboard_getkey,
+     .getkeystatus = grub_usb_keyboard_getkeystatus,
+     .next = 0
+   };
 -  grub_usb_device_t usbdev;
 -  grub_uint8_t status;
 -  int key;
 -  struct grub_usb_desc_endp *endp;
 -};
++static struct grub_term_input grub_usb_keyboards[16];
++
++static int
++interpret_status (grub_uint8_t data0)
+ {
 -static struct grub_term_input grub_usb_keyboards[16];
++  int mods = 0;
 -                      USB_HID_SET_PROTOCOL, 0, 0, 0, 0);
++  /* Check Shift, Control, and Alt status.  */
++  if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
++    mods |= GRUB_TERM_STATUS_LSHIFT;
++  if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
++    mods |= GRUB_TERM_STATUS_RSHIFT;
++  if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
++    mods |= GRUB_TERM_STATUS_LCTRL;
++  if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
++    mods |= GRUB_TERM_STATUS_RCTRL;
++  if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
++    mods |= GRUB_TERM_STATUS_LALT;
++  if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
++    mods |= GRUB_TERM_STATUS_RALT;
++
++  return mods;
++}
+ static void
+ grub_usb_keyboard_detach (grub_usb_device_t usbdev,
+                         int config __attribute__ ((unused)),
+                         int interface __attribute__ ((unused)))
+ {
+   unsigned i;
+   for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
+     {
+       struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
+       if (!data)
+       continue;
+       if (data->usbdev != usbdev)
+       continue;
++      if (data->transfer)
++      grub_usb_cancel_transfer (data->transfer);
++
+       grub_term_unregister_input (&grub_usb_keyboards[i]);
+       grub_free ((char *) grub_usb_keyboards[i].name);
+       grub_usb_keyboards[i].name = NULL;
+       grub_free (grub_usb_keyboards[i].data);
+       grub_usb_keyboards[i].data = 0;
+     }
+ }
+ static int
+ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
+ {
+   unsigned curnum;
+   struct grub_usb_keyboard_data *data;
+   struct grub_usb_desc_endp *endp = NULL;
+   int j;
+   grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
+               usbdev->descdev.class, usbdev->descdev.subclass,
+               usbdev->descdev.protocol, configno, interfno);
+   for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
+     if (!grub_usb_keyboards[curnum].data)
+       break;
+   if (curnum == ARRAY_SIZE (grub_usb_keyboards))
+     return 0;
+   if (usbdev->descdev.class != 0 
+       || usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
+     return 0;
+   if (usbdev->config[configno].interf[interfno].descif->subclass
+       != USB_HID_BOOT_SUBCLASS
+       || usbdev->config[configno].interf[interfno].descif->protocol
+       != USB_HID_KBD_PROTOCOL)
+     return 0;
+   for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
+        j++)
+     {
+       endp = &usbdev->config[configno].interf[interfno].descendp[j];
+       if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
+         == GRUB_USB_EP_INTERRUPT)
+       break;
+     }
+   if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
+     return 0;
+   grub_dprintf ("usb_keyboard", "HID found!\n");
+   data = grub_malloc (sizeof (*data));
+   if (!data)
+     {
+       grub_print_error ();
+       return 0;
+     }
+   data->usbdev = usbdev;
++  data->interfno = interfno;
+   data->endp = endp;
+   /* Place the device in boot mode.  */
+   grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
 -                      USB_HID_SET_IDLE, 0<<8, 0, 0, 0);
++                      USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
+   /* Reports every time an event occurs and not more often than that.  */
+   grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
 -                              USB_HID_GET_REPORT, 0x0000, interfno,
++                      USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
+   grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
+              sizeof (grub_usb_keyboards[curnum]));
+   grub_usb_keyboards[curnum].data = data;
+   usbdev->config[configno].interf[interfno].detach_hook
+     = grub_usb_keyboard_detach;
+   grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
+   if (!grub_usb_keyboards[curnum].name)
+     {
+       grub_print_error ();
+       return 0;
+     }
++  /* Test showed that getting report may make the keyboard go nuts.
++     Moreover since we're reattaching keyboard it usually sends
++     an initial message on interrupt pipe and so we retrieve
++     the same keystatus.
++   */
++#if 0
+   {
+     grub_uint8_t report[8];
+     grub_usb_err_t err;
+     grub_memset (report, 0, sizeof (report));
+     err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
 -      {
 -      data->status = 0;
 -      data->key = -1;
 -      }
++                              USB_HID_GET_REPORT, 0x0100, interfno,
+                               sizeof (report), (char *) report);
+     if (err)
 -      {
 -      data->status = report[0];
 -      data->key = report[2] ? : -1;
 -      }
++      data->status = 0;
+     else
 -grub_usb_keyboard_checkkey (struct grub_term_input *term)
++      data->status = report[0];
+   }
++#else
++  data->status = 0;
++#endif
++
++  data->transfer = grub_usb_bulk_read_background (usbdev,
++                                                data->endp->endp_addr,
++                                                sizeof (data->report),
++                                                (char *) data->report);
++  if (!data->transfer)
++    {
++      grub_print_error ();
++      return 0;
++    }
++
++  data->last_key = -1;
++  data->mods = 0;
++  data->dead = 0;
+   grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
+   return 1;
+ }
\f
++static void
++send_leds (struct grub_usb_keyboard_data *termdata)
++{
++  char report[1];
++  report[0] = 0;
++  if (termdata->mods & GRUB_TERM_STATUS_CAPS)
++    report[0] |= LED_CAPS_LOCK;
++  if (termdata->mods & GRUB_TERM_STATUS_NUM)
++    report[0] |= LED_NUM_LOCK;
++  grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
++                      USB_HID_SET_REPORT, 0x0200, termdata->interfno,
++                      sizeof (report), (char *) report);
++  grub_errno = GRUB_ERR_NONE;
++}
++
+ static int
 -  grub_uint8_t data[8];
++grub_usb_keyboard_getkey (struct grub_term_input *term)
+ {
 -  if (termdata->key != -1)
 -    return termdata->key;
+   grub_usb_err_t err;
+   struct grub_usb_keyboard_data *termdata = term->data;
++  grub_uint8_t data[sizeof (termdata->report)];
+   grub_size_t actual;
 -  data[2] = 0;
++  if (termdata->dead)
++    return GRUB_TERM_NO_KEY;
 -  err = grub_usb_bulk_read_extended (termdata->usbdev,
 -                                   termdata->endp->endp_addr, sizeof (data),
 -                                   (char *) data, 10, &actual);
 -  if (err || actual < 1)
 -    return -1;
+   /* Poll interrupt pipe.  */
 -  termdata->status = data[0];
++  err = grub_usb_check_transfer (termdata->transfer, &actual);
 -  if (actual < 3 || !data[2])
 -    return -1;
++  if (err == GRUB_USB_ERR_WAIT)
++    {
++      if (termdata->last_key != -1
++        && grub_get_time_ms () > termdata->repeat_time)
++      {
++        termdata->repeat_time = grub_get_time_ms ()
++          + GRUB_TERM_REPEAT_INTERVAL;
++        return termdata->last_key;
++      }
++      return GRUB_TERM_NO_KEY;
++    }
++
++  grub_memcpy (data, termdata->report, sizeof (data));
++
++  termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
++                                                    termdata->endp->endp_addr,
++                                                    sizeof (termdata->report),
++                                                    (char *) termdata->report);
++  if (!termdata->transfer)
++    {
++      grub_printf ("%s failed. Stopped\n", term->name);
++      termdata->dead = 1;
++    }
 -              "report: 0x%02x 0x%02x 0x%02x 0x%02x"
++  termdata->last_key = -1;
+   grub_dprintf ("usb_keyboard",
 -  /* Check if the Control or Shift key was pressed.  */
 -  if (data[0] & 0x01 || data[0] & 0x10)
 -    termdata->key = keyboard_map[data[2]] - 'a' + 1;
 -  else if (data[0] & 0x02 || data[0] & 0x20)
 -    termdata->key = keyboard_map_shift[data[2]];
 -  else
 -    termdata->key = keyboard_map[data[2]];
++              "err = %d, actual = %d report: 0x%02x 0x%02x 0x%02x 0x%02x"
+               " 0x%02x 0x%02x 0x%02x 0x%02x\n",
++              err, actual,
+               data[0], data[1], data[2], data[3],
+               data[4], data[5], data[6], data[7]);
 -  if (termdata->key == 0)
 -    grub_printf ("Unknown key 0x%x detected\n", data[2]);
++  if (err || actual < 1)
++    return GRUB_TERM_NO_KEY;
 -  grub_errno = GRUB_ERR_NONE;
++  termdata->status = data[0];
 -  return termdata->key;
 -}
++  if (actual < 3)
++    return GRUB_TERM_NO_KEY;
 -static int
 -grub_usb_keyboard_getkey (struct grub_term_input *term)
 -{
 -  int ret;
 -  struct grub_usb_keyboard_data *termdata = term->data;
++  if (data[2] == KEY_NO_KEY || data[2] == KEY_ERR_BUFFER
++      || data[2] == KEY_ERR_POST || data[2] == KEY_ERR_UNDEF)
++    return GRUB_TERM_NO_KEY;
 -  while (termdata->key == -1)
 -    grub_usb_keyboard_checkkey (term);
++  if (data[2] == KEY_CAPS_LOCK)
++    {
++      termdata->mods ^= GRUB_TERM_STATUS_CAPS;
++      send_leds (termdata);
++      return GRUB_TERM_NO_KEY;
++    }
 -  ret = termdata->key;
++  if (data[2] == KEY_NUM_LOCK)
++    {
++      termdata->mods ^= GRUB_TERM_STATUS_NUM;
++      send_leds (termdata);
++      return GRUB_TERM_NO_KEY;
++    }
 -  termdata->key = -1;
++  termdata->last_key = grub_term_map_key (data[2], interpret_status (data[0])
++                                        | termdata->mods);
++  termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
 -  return ret;
++  grub_errno = GRUB_ERR_NONE;
 -  int mods = 0;
 -
 -  /* Check Shift, Control, and Alt status.  */
 -  if (termdata->status & 0x02 || termdata->status & 0x20)
 -    mods |= GRUB_TERM_STATUS_SHIFT;
 -  if (termdata->status & 0x01 || termdata->status & 0x10)
 -    mods |= GRUB_TERM_STATUS_CTRL;
 -  if (termdata->status & 0x04 || termdata->status & 0x40)
 -    mods |= GRUB_TERM_STATUS_ALT;
++  return termdata->last_key;
+ }
+ static int
+ grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
+ {
+   struct grub_usb_keyboard_data *termdata = term->data;
 -  return mods;
++  return interpret_status (termdata->status) | termdata->mods;
+ }
+ struct grub_usb_attach_desc attach_hook =
+ {
+   .class = GRUB_USB_CLASS_HID,
+   .hook = grub_usb_keyboard_attach
+ };
+ GRUB_MOD_INIT(usb_keyboard)
+ {
+   grub_usb_register_attach_hook_class (&attach_hook);
+ }
+ GRUB_MOD_FINI(usb_keyboard)
+ {
+   unsigned i;
+   for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
+     if (grub_usb_keyboards[i].data)
+       {
++      struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
++
++      if (!data)
++        continue;
++      
++      if (data->transfer)
++        grub_usb_cancel_transfer (data->transfer);
++
+       grub_term_unregister_input (&grub_usb_keyboards[i]);
+       grub_free ((char *) grub_usb_keyboards[i].name);
+       grub_usb_keyboards[i].name = NULL;
++      grub_free (grub_usb_keyboards[i].data);
+       grub_usb_keyboards[i].data = 0;
+       }
+   grub_usb_unregister_attach_hook_class (&attach_hook);
+ }