--- /dev/null
+ 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)';
+ };
--- /dev/null
+ 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
--- /dev/null
+ 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;
++};
--- /dev/null
-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);
+ }
--- /dev/null
- /* 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);
+ }
--- /dev/null
- 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;
+ }
--- /dev/null
- 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);
+ }
--- /dev/null
- (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);
+ }
--- /dev/null
--- /dev/null
++/*
++ * 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);
++}
--- /dev/null
-#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);
+ }
--- /dev/null
- 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);
+ }
--- /dev/null
--- /dev/null
++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
--- /dev/null
-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);
+ }
--- /dev/null
--- /dev/null
++/*
++ * 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
++
--- /dev/null
-/* 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
--- /dev/null
+ /*
+ * 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);
+ }
+
--- /dev/null
- 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);
+ }
+ }
--- /dev/null
-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);
+ }
--- /dev/null
- 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
--- /dev/null
--- /dev/null
++/*
++ * 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
--- /dev/null
--- /dev/null
++/*
++ * 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)
--- /dev/null
--- /dev/null
++/*
++ * 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 (¶ms->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);
++}
--- /dev/null
--- /dev/null
++/* 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);
++}
--- /dev/null
--- /dev/null
++/*
++ * 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)
--- /dev/null
- 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;
+ }
--- /dev/null
- 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;
+ }
--- /dev/null
- {"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);
+ }
--- /dev/null
- 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;
+ }
--- /dev/null
- 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 ();
+ }
--- /dev/null
-#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;
+ }
--- /dev/null
-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);
+ }
--- /dev/null
-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);
+ }
--- /dev/null
-#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);
+ }
--- /dev/null
- .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 ();
+ }
--- /dev/null
- .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);
+ }
--- /dev/null
- 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);
+ }
--- /dev/null
-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);
+ }